home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Collections: Purity
/
Purity #21 (1994-01-12)(Diesel)(DE)[WB].zip
/
Purity #21 (1994-01-12)(Diesel)(DE)[WB].adf
/
PCQ-Dokus
/
PCQ_1.2b.Anl
< prev
next >
Wrap
Text File
|
1993-12-13
|
129KB
|
2,964 lines
DEUTSCHE ANLEITUNG ZUM PCQ-PASCAL-COMPILER V1.2b VON PATRICK QUAID
==================================================================
Diese deutsche Anleitung wurde von Stefan Grad (GPD), Bahnhofstr. 37, 78588
Denkingen, Deutschland verfaßt. Dabei wurde die deutsche Anleitung zur V1.1
von der Purity #3 verwendet (verfaßt von einem gewissen "Olli"). Dieser be-
ruft sich wiederum auf die Anleitung zur V1.0 von "Vinorkis ^O^".
Die Anleitung ist jetzt fast vollständig (so etwa 90% der engl. Anleitung
wurde übersetzt und noch ein paar Sachen hinzugefügt; diese Hinzufügungen
könnt Ihr am Zusatz Anm.d.Ü. (Anmerkung des Übersetzers) erkennen).
Das © Copyright der Anleitung liegt bei Patrick Quaid und mir (1993). Sie
darf nur gemeinsam mit dem Compiler weitergegeben werden (ausgenommen davon
ist die Purity: hier liegt PCQ 1.2b auf Disk 6 vor), wobei dann alle Copy-
rightbestimmungen vom Kapitel 24 erfüllt sein müssen.
Nun aber zur Anleitung...
===========================================================================
PCQ
Version 1.2
Ein Pascal-Compiler für den Amiga
von Patrick Quaid
===========================================================================
PCQ (das bedeutet Pascal Compiler, ähem, Q.... Naja, dem Autor fiel kein Name
ein, so hat er einfach seine Initalien benutzt, ok?) ) ist ein einfacher Pas-
cal-Compiler für den Amiga. Er ist zwar kein Public-Domain-Program im eigent-
lichen Sinne (denn der Autor behält sich das Copyright auf den Sourcecode,
die runtime.library mit deren Sourcecode, den Compiler und der Dokumentation
vor), aber es darf weitergegeben werden, solange der Preis angemessen ist
(siehe auch der letzte Abschnitt).
Die Features des Compilers:
Das Schlechte:
- Er unterstützt keine Sets.
- Er ist nicht kompatibel zu TurboPascal, obwohl er sich immer mehr annähert.
- Manche Sachen sind leicht anders als im Standard-Pascal.
- Die meisten Fehler erzeugen eine Endlosschleife.
Das Positive:
- Er funktioniert.
- Der Compiler unterstützt Include-Files
- Er erlaubt externe Kompilierung, obwohl diese von Hand geprüft
werden müssen (das hier ist kein Modula-2)
- Unterstützt Strukturen (Records), Aufzählungstypen (enumerated types),
Zeiger (Pointers), Arrays und Strings.
- Typen-Konvertierungen wie in Modula-2 werden unterstützt. Mit anderen
Worten: Ausdrücke wie "integer('d')" sind möglich. Einige Besonderheiten
- Einige Besonderheiten von Turbo und Quick-Pascal wie "Exit" innerhalb einer
Prozedur, Operatoren wie "Shl" und "Shr" und Typen-Konstanten sind
eingebaut worden.
- Es können beliebig viele const, var, type, prcödure und function-Blöcke
definiert werden, in jeder Reihenfolge.
- Er kostet nichts (solange sie nicht auf gewisse Sonderangebote deutscher
PD-Händler reinfallen, Anm.d.Ü.).
IHNALTSVERZEICHNIS
^^^^^^^^^^^^^^^^^^
Diese Bedienungsanleitung sollte mit einem sog. File-Reader, also einem
speziellen ASCII-Datei-Leseprogramm oder einem Text-Editor gelesen werden.
Deshalb hier keine Seiten-, sondern Zeilennummern (ohne Gewähr, Anm.d.Ü. :-)
Abschnitt (Sektion) Zeile
1. Arbeiten mit dem PCQ .................................. 138
2. Inkompatibilitäten .................................... 245
3. Reservierte Worte ..................................... 303
4. Pascal-Programm-Struktur .............................. 326
5. Vordefinierte Elemente ................................ 385
5. 1 Konstanten (CONST) .................................... 412
5. 2 Typen (TYPE) .......................................... 505
5. 3 Variablen (VAR) ....................................... 570
5. 3.1 Standard-Variablen .................................... 592
5. 4 Prozeduren und Funktionen - Deklarationen ............. 641
5. 4.1 Parameterübergabe ..................................... 668
5. 4.2 Vor-Definition/Referenz von Prozeduren und Funktionen . 702
5. 4.3 Externe Verweise (Referenzen) ......................... 724
5. 4.4 Prozeduren (PROCEDURE) ................................ 743
5. 4.5 Funktionen (FUNCTION) ................................. 881
6. Namen (Label) - Deklarationen ......................... 1014
7. Spezielle Befehle ..................................... 1031
8. Ausdrücke ............................................. 1069
8. 1. Berechnungsreihenfolge ................................ 1218
8. 2. Konstanten-Ausdrücke .................................. 1247
8. 3. Referenz-Variablen .................................... 1256
9. Statements ............................................ 1292
9. 1. IF-Anweisung .......................................... 1317
9. 2. WHILE-Anweisung ....................................... 1346
9. 3. REPEAT-Anweisung ...................................... 1368
9. 4. FOR-Anweisung ......................................... 1388
9. 5. CASE-Anweisung ........................................ 1430
9. 6. WITH-Anweisung ........................................ 1466
9. 7. COMPOUND-Anweisung .................................... 1499
9. 8. ASSIGNMENT-Anweisung .................................. 1512
9. 9. PROCEDURE-Aufrufe ..................................... 1524
9.10. RETURN-Befehl ......................................... 1551
9.11. GOTO-Anweisung ........................................ 1569
10. Ein-und Ausgabe ....................................... 1596
10. 1. Text- und Typen-Files ................................. 1632
10. 2. Öffnen von Files ...................................... 1677
10. 3. Schreiben in Files .................................... 1753
10. 4. Lesen aus Files ....................................... 1807
10. 5. File-Puffer ........................................... 1880
10. 6. Ein- und Ausgabe-Überprüfung (IO-Checken) ............. 1934
10. 7. Standard Ein & Ausgabe ................................ 1960
11. Zeichenkettenvariablen (Strings) ...................... 2023
11. 1. Reservieren von String-Speicher ....................... 2055
11. 2. Benutzung von Escape-Zeichen .......................... 2099
11. 3. StringLib ............................................. 2143
12. Speicher-Verwaltung ................................... 2154
12. 1. HeapError (Überlauf-Fehler) ........................... 2187
13. Exit-Prozeduren ....................................... 2214
14. Laufzeitfehler (Runtime-Errors) ....................... 2257
15. Compiler-Direktiven ................................... 2318
16. Typen-Konvertierungen ................................. 2435
17. Kurzer Anfangscode (Small Initialisation Code) ........ 2473
18. Externe Files ......................................... 2524
19. Einige Anmerkungen für Assembler-Programmierer ........ 2588
20. Fehlermeldungen ....................................... 2612
21. Quellen ............................................... 2643
22. Zukünftige Erweiterungen .............................. 2707
23. Update-History ........................................ 2729
24. Weitere Anmerkungen, Copyright und Adressen ........... 2738
25. Stichwortverzeichnis .................................. 2797
25. 1. Allgemeine Begriffe ................................... 2808
25. 2. Mathematische Operatoren .............................. 2899
25. 3. Compiler-Direktiven ................................... 2923
26. Abschließende Kurzinformationen ....................... 2936
1. Arbeiten mit dem PCQ
^^^^^^^^^^^^^^^^^^^^^^^
Auf dieser Diskette befinden sich eine ganze Menge Files, die Sie sich
vor dem ersten Einsatz des PCQ erst einmal auf eine Arbeitsdiskette
kopieren sollten. Eines davon ist natürlich der Compiler selbst (Pascal),
selbstverständlich ebenso die Laufzeit-Bibliothek (PCQ.lib). Außerdem müßt
Ihr Euch den Assembler (A68k) und den Linker (Blink) ebenso auf Eure Arbeits-
disk kopieren. Diese hier aufgelisteten Files sind sogar für die einfachsten
Programme unbedingt erforderlich.
Alle Files mit dem Suffix ".p" sind Pascal-Beispielprogramme, die Ihr
Euch ebenfalls auf Eure Arbeitsdisk kopieren könnt. Der Autor arbeitetet
natürlich wesentlich mehr am Compiler selbst, als an diesen Beispielen,
einige von diesen sind jedoch vor allem für diejenigen von Euch
interessant, die noch nie mit Pascal gearbeitet haben. Diese Programme
demonstrieren alle Möglichkeiten des Compilers, die dem Autor bis dato
eingefallen sind, deswegen solltet Ihr Euch diese Ansehen, bevor Ihr sie
löscht. Der Source-Code des Compilers besteht ebenfalls aus einer Anzahl
Files mit dem Suffix ".p".
Alle Files mit dem Suffix ".i" sind Include-Files für einige System-
Bibliotheken. Diese definieren die Typen, Konstanten, Prozeduren,
Funktionen, Variablen und Records, die für System-Zugriffe erforderlich
sind. Diese solltet Ihr nach Möglichkeit mit auf Eure Arbeitsdisk in ein
Unterverzeichnis mit dem Namen "Include" kopieren. Auch einige
Include-Files für Routinen in der "PCQ.lib" gehören dazu. Schauen Sie sich
diese genauer an, da hier öfters etwas geändert wird. Der Code für diese
Routinen ist in der "PCQ.lib".
Um nun ein Programm zu compilieren, müssen Sie erst eines mit einem
Texteditor (ich empfehle den AZ-Editor auf der GPD-Disk 40, den Ihr gegen
3 DM bei Stefan Grad (Adresse s.o.) bekommen könnt - Anm.d.Ü.) schreiben,
oder eines der Beispielprogramme nehmen. Dann geben Sie ein:
1> Pascal prog.p prog.asm {-q}
wobei "Pascal" natürlich der Compiler selbst ist (den Namen können Sie
nach belieben ändern). "prog.p" ist der Name der Quell-Datei des
Pascalprogrammes, der natürlich auch beliebig sein kann. Der zweite
Paramter ist dann der Name des zu erzeugenden Assembler-Files. Die "-q"-
Direktive bewirkt, das der Compiler nur noch Fehlermeldungen ausgibt, und
diese in komprimierter Form.
Beim Compilieren der Beispielprogramme kann es Probleme mit der Struktur
der Unterverzeichnisse geben. Die Beispiele verlangen alle die
Include-Files in einem Verzeichnis ":Include/BlaBla.i". Falls das Probleme
mit der Ihrer Konfiguratiön aufwirft, müßen Sie die Include-Befehle am
Anfang der Programme ändern.
Hat der Compiler keine Fehlermeldung ausgegeben, geben Sie ein:
1> A68k prog.asm prog.o
Der Assembler erzeugt dann den Object-Code. Sollte sich auf dieser Diskette
der A68k-Assembler befinden, dann sollten Sie darauf auch die entsprechende
englischsprachige Anleitung zu diesem Assembler finden ("A68k.doc"). Falls
nicht, benutzen Sie die Version V2.6 des A68k-Assemblers von Charlie Gibbs.
Dieser Assembler führt viele kleinere Optimierungen durch, auf die sich
der PCQ mehr oder weniger verlässt. Deshalb kann ich nicht behaupten, daß
der PCQ-Compiler auch mit anderen Assemblern zusammenarbeitet. Am Ende
müssen Sie nun das Programm "Linken", also geben Sie nun ein:
1> Blink prog.o small.lib to prog library PCQ.lib
Hier wird nun also endlich die eigentliche ausführbare Datei mit dem Namen
'prog' erzeugt. Sämtliche Pascal-Laufzeit-Routinen, die Amiga-
System-Routinen und meine eigene, kleine String-Bibliothek sind in der
PCQ.lib enthalten. Sollten manche Namen von Routinen mit denjenigen, mit
denen Sie selbst arbeiten, kollidieren, dann stellen Sie einfach Ihre
eigenen Bibilothek-oder Object-Files in der Blink-Kommandozeile VOR die
PCQ.lib. Sollten Sie noch Fragen zum Linken haben, dann sehen Sie bitte in
der entsprechenden Dokumentation zu Blink mit dem Namen 'Blink.doc' nach,
die sich ebenfalls auf dieser Diskette befindet.
Der Autor selbst verwendet die Version 6.7 von "Blink" und zweifelt auch
hier, ob der PCQ auch mit anderen Versionen oder sogar anderen Linkern
zusammenarbeitet. "Small.lib" ist eine kleine Offsetbibliothek, die von
Matt Dillon geschrieben wurde. Da in "Blink" ein kleiner Fehler existiert,
muß diese Datei als Object-File statt als Bibliothek eingebunden werden.
Dies erhöht aber nicht die Code-Größe des Programms.
Statt nun aber immer diese Folge von Befehlen eingeben zu müssen, können
Sie auch die "make"-Batch-Datei aufrufen, die sich ebenfalls auf der
Original-Pascal Diskette befinden müsste und Ihnen die eklige Tipparbeit
abnimmt. Sie sollten sich dieses File jedoch vorher einmal ansehen, denn es
könnte sein, dass Sie den ein oder anderen Pfad an die Verzeichnis-Strukutr
Ihrer Arbeitsdiskette anpassen müssen. Es ist jedoch wichtig, die
geänderte Version der "make"-Datei unbedingt wieder als Script-File
kennzeichen ("protect make +s"), da diese sonst vom AmigaDOS 1.3 oder ARP
nicht als Batch-Datei anerkannt wird. Aufgerufen wird diese dann wie folgt:
1> make prog
Dabei wird dann die Datei 'prog.p' automatisch compiliert, assembliert und
anschliessend zur ausführbaren Datei 'prog' gelinkt, ohne dass Sie mehr
eingeben müssen. Sollten Sie in Ihr Programm natürlich andere, gesondert
compilierte Units eingebunden haben, dann müssen Sie diese Batch-Datei
natürlich ändern oder eine neü schreiben. Ich schlage Ihnen vor, daß Sie
sich für jedes Programm, das Sie mehr als einmal compilieren wollen oder
müssen (vor allem in der Entwicklungsphase) eigene Batch-Dateien schreiben,
die natürlich dann andere Namen haben. Sollten Sie damit Schwierigkeiten
haben, dann wenden Sie sich bitte an mich. Ich kann Ihnen dann hoffentlich
eine ausführlichere Antwort auf Ihre Fragen geben.
2. Inkompatibilitäten
^^^^^^^^^^^^^^^^^^^^^
PCQ-Pascal ist inkompatibel mit dem Standard-Pascal und Turbo-Pascal.
Kurz, diese sind:
1) Sets werden nicht unterstützt.
2) Die Standard-Pascal-Deklaration von Zeigern auf Records wird nicht unter-
stützt. Z.B. der folgende Syntax ist im Standard-Pascal erlaubt:
type
WindowPtr = ^Window;
Window = record
NextWindow : WindowPtr;
...
Dies bricht in der ersten Zeile mit einem "Unknown ID"-Fehler ab. Benutzen
Sie stattdessen folgendes:
type
Window = record
NextWindow : ^Window;
....
end;
WindowPtr = ^Window;
3) Auch variante Records sind immer noch verboten.
4) Der bekannte Syntax, um ein einzelnes Anführungszeichen zu
erzeugen (''''), wird ebenfalls nicht akzeptiert. PCQ benutzt die
C-Escape-Funktion: ('\''). Näheres darüber in dem Abschnitt "Strings".
5) Die Art, wie man ein File öffnet, ist anders als im Standard-Pascal, ob-
wohl es einmal geöffntet im Grunde genommen dieselben Befehle sind.
Obwohl PCQ-Pascal nicht dafür gemacht wurde, um Turbo-Pascal kompatibel zu
sein, werden hier trotzdem einmal die wichtigeren Unterschiede näher erklärt
(die natürlich zu den obigen hinzukommen), um die Portierung dieser Programme
einfacher zu machen.
1) PCQ-Pascal behandelt Strings komplett anders als Turbo-Pascal. PCQ-Strings
sind C-Strings ähnlich, die nicht so einfach manipuliert werden können
als Turbo-Strings. Tatsächlich sind PCQ-Strings die schwierigsten von al-
len 3. Sie werden aber in der Strings-Sektion näher erklärt.
2) In einer PCQ-Pascal-Funktion, führt das zuweisen eines Wertes zu einem
Funktionswert zum Verlassen der Funktion. In Turbo-Pascal wird der Funk-
tionsname wie eine Write-Only-Variable (nur beschreibbar) behandelt und
man muß den Exit-Befehl verwenden. Alle Beispiele im Pascal-Report um-
säumen die Ausgabe, indem sie den Wert als das letzte Statement assignen
(?!?).
3) Exit verläßt in PCQ das Programm, während es in Turbo die aktuelle Funk-
tion oder Prozedur verläßt. Turbos "Exit" ist wie PCQs "Return" und Tur-
bos "Halt" ist wie PCQs "Exit". Alles klar?
3. Reservierte Worte
^^^^^^^^^^^^^^^^^^^^
Folgende Worte sind beim PCQ reserviert:
and for procedure
array forward program
begin function record
by goto repeat
case if return
const in set
div label then
do mod to
downto not type
else of until
end or var
external packed while
file private with
Auch nichtimplementierte Wörter sind reserviert!
4. Pascal-Programm-Struktur
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Die normale Pascal-Programm-Struktur ist wie folgt:
<Program> ::= Program <Identifier> ; <Block>. |
Program <Identifier> (<Identifier List>); <Block>.
<Block> ::= <Declarations> begin <Statements> end |
begin <Statements> end
<Identifier List> ::= <Identifier> , <Identifier List> |
<Identifier>
::= heißt: ist definiert durch
< > heißt: wird irgendwo anders in der Liste definiert
| trennt zwischen Alternativen
(WICHTIG !!! - Diese Definitionen tauchen in der weiteren Anleitung immer
wieder auf! - Anm.d.Ü.)
Ein Pascal-Programm beginnt somit mit dem Wort "program" gefolgt von einem
Identifier, gefolgt von einem Semikolon, gefolgt von einem Block und einem
Punkt am Ende. Es kann aber auch aus "program", gefolgt von einem Identifier
und einer linken Klammer sowie einer Identifier-Liste und einer rechten Klam-
mer, einem Semikolon, einem Block und schließlich einem Punkt bestehen.
Bisher undefiniert sind:
<Declarations> ::= <Declaration> , <Declarations> | <Declaration>
<Declaration> ::= <Type Declaration> |
<Constant Declaration> |
<Variable Declaration> |
<Label Declaration> |
<Procedure Declaration> |
<Function Declaration>
Beachtet dabei, daß Standard-Pascal eine Ordnung der Deklarationen verlangt:
Es erwartet einen Konstantenblock, gefolgt von einem Typenblock usw. In PCQ
ist dem nicht so, wodurch Ihr so viel Deklarationen in einer beliebigen Ord-
nung haben könnt, wie Ihr wollt.
Dies ist das normale Format eines jeden PCQ-Pascal-Programmes. Es gibt aller-
dings eine Ausnahme (natürlich): die separat kompilierbaren Files. Turbo-Pas-
cal benutzt hierzu eine Unit-Struktur, wodurch man Units schreiben, kompilie-
ren und debuggen kann, um sie anschließend in vielen Programmen zu benutzen.
PCQ bietet eine ähnliche, wenn auch nicht so mächtige, Funktion in der Form
externer Files. Die Struktur:
<Program> ::= Program <Identifier>; <Block>. |
Program <Identifier> (<Identifier List>); <Block>. |
External ; <Declarations>
Somit muß also ein externes File nur das reservierte Wort "External" gefolgt
von einem Semikolon enthalten, gefolgt von Serien von Prozeduren, Funktionen,
usw. Siehe die Sektion "Externe Files" für mehr Infos.
5. Vordefinierte Elemente
^^^^^^^^^^^^^^^^^^^^^^^^^
Nachfolgend ist eine Anordnung der Elemente, wie sie in Pascal üblich ist.
Nur könnt Ihr im PCQ diese Blocks in jeder beliebigen Reihenfolge schreiben,
und vor allem mehr als einmal jeden Block definieren. Mit anderen Worten:
Eure Programme können z.B so aussehen:
Program name;
var
Variablendeklarationen
type
Typen
var
mehr Variablen
procedure
eine Prozedur
var
noch mehr Variablen...
Und so weiter... Natürlich muß ein Element nach wie vor erst einmal dekla-
riert werden, bevor es gebraucht wird. Das wurde zugelassen, da es wirklich
übel ist, einen Rattenschwanz von Include-Files zu ordnen (jedes einzelne
Include-File hätte sonst in vier Abschnitte zerlegt werden müssen: In die
Konstanten, die Typen, die Variablen und die Prozeduren und Funktionen).
5.1 Konstanten (CONST)
----------------------
Die Konstanten folgen folgender Struktur:
<Const Block> ::= Const <Const Declarations>;
<Const Declarations> ::= <Const Declaration> |
<Const Declaration> ; <Const Declarations>
<Const Declaration> ::= <Identifier> = <Constant Expression> |
<Identifier> : <Type Specification> =
<Typed Constant Value>
<Typed Constant Value> ::= <Constant Expression> |
( <Constant Expressions> ) |
@ <Identifier>
Die Werte für True und False werden als -1 und 0 definiert. Nil (Null)
wird definiert als Zeiger mit dem konstanten Wert 0, ist jedoch kein
reserviertes Wort wie im Standard-Pascal.
Meist, wenn der Compiler eine Konstante verlangt, nimmt er dafür einen
konstanten Ausdruck (der schon während der Compilierung ausgewertet wird).
Beispielsweise funktioniert Folgendes:
const
first = 234;
second = first * 2;
type
thetype = array [first .. first + 7] of char;
Normale Konstanten sind einfach Read-Only-Variablen (nur lesbar), die keinen
Speicher wegnehmen. Der Konstantenwert wird nur im Programm anstatt des Iden-
tifiers eingefügt. Konstanten können vom Typ INTEGER, REAL, CHAR, BOOLEAN,
STRING und ARRAY OF CHAR sein, nicht dagegen benutzerdefinierte Typen bzw.
Record- oder Array-Typen (Feldvariablen).
Es gibt einige Unterschiede zwischen diesem und dem TurboPascal-Syntax (na-
türlich alle zum Vorteil des PCQ :-) . Zunächst einmal müssen in Turbo bei
der Definition von Konstanten-Records aus Feld-Identifier, gefolgt von einem
Doppelpunkt, dem Wert und möglicherweise einem Semikolon bestehen. In PCQ
muß hingegen nur dasselbe wie bei Feldern (arrays) gemacht werden: alle
Felder werden mit Kommas getrennt spezifiziert.
Bei Turbo bis V6.0 mußten Zeiger-Typen nach NIL: initialisiert werden. PCQ
erlaubt die initialisierung eines Zeigertyps auf Nil oder auf die Adresse
einer globalen Variable oder Typenkonstante. Um dies zu tun, muß man norma-
lerweise den "@"-Operator benutzen, der die Adresse einer spezifizierten Va-
riable zurückgibt.
Der letzte Unterschied ist, daß einige typifizierte Konstantenwerte in unter-
sequenten Konstantenausdrücken verwendet werden können. Strukturisierte Typen
(z.B. Records und Arrays) können nicht verwendet werden. Verwendet werden
können also Integer, Real und andere sog. einfachen Typen.
Es gibt einige Standard-Konstanten (z.B. die in dem Compiler selbst eingebau-
ten), die man nicht deklarieren oder per Include-File benutzen muß.
Diese sind:
False ist vom Typ Boolean und hat den Wert 0
MaxInt ist als $7FFFFFFF definiert (dezimal 2.147.483.647), der im 32-
Bit-Integer-Typ "untergebracht" werden kann. Somit kann eine In-
teger-Variable einen Wert von +MaxInt bis -MaxInt beinhalten.
MaxShort ist die größte Zahl, die im 16-Bit-Kurzzahl-Typ untergebracht
werden kann. Es ist 32767, oder $7FFF in hex. Eine Short-Variable
kann also Werte von +MaxShort bis -MaxShort enthalten (d.h. bis
-(Maxshort+1), weil MaxInt analog ist).
Nil ist eine Konstante vom Typ Adresse. Sie ist wie folgt definiert:
Nil=Address(0);
Im Standard-Pascal ist "Nil" ein reserviertes Wort, was aber
nicht sein muß. Daher ist es in PCQ auch nicht reserviert!
True ist vom Typ Boolean und hat den Wert -1.
In manchen Fällen genügt, daß es sich um eine Zahl ungleich Null
handelt, aber der "not"-Operator verhält sich falsch bei irgend-
welchen anderen Werten.
Eine andere Form von Konstanten sind "Typen-Konstanten". Dies sieht
folgendermassen aus:
CONST
Identifier : Type Description = Constant Expression;
Typenkonstanten werden zu Beginn des Programms mit der "Constant
Expression" initialisiert und können dann genauso wie Variablen benutzt
werden. Dies wird im Abschnitt "Typenkonstanten" genaür beschrieben.
5.2 Typen (TYPE)
----------------
Es gibt verschiedene vordefinierte Typen.
Integer 4 Bytes, von +/- MaxInt
Short 2 Bytes. Zahlen innerhalb des Programmtexts
werden als Short-Werte angesehen, sofern sie nicht
grösser als 32767 oder kleiner als - 32767 sind.
Byte 1 Byte. Bei diesen drei Typen handelt es sich
ausnahmslos um numerische Typen, sodaß Sie sie in normalen
Ausdrücken verwenden können, ohne sich um
Typenkonvertierungen Gedanken machen zu müssen. Der Compiler
wandelt die kleinen Werte automatisch auf die verlangte
Grösse um. Denken Sie aber daran, dass momentan noch kein
Überlaufgeprüft wird. Ab Version 1.1 haben Byte-Werte den
Bereich 0..255 statt -128..127.
Real 4 Bytes (FFP-Format).
Char 1 Byte.
Boolean 1 Byte. False ist 0, Trü ist -1.
String 4 Bytes. Als "^char" definiert. Wird im
Abschnitt "Strings" noch näher erläutert.
Address 4 Bytes. Dies ist ein Zeiger auf keinen besonderen
Typ und ist zu jedem anderen Zeiger typenkompatibel
Tatsächlich ist die Konstante Nil vom Typ Address.
Text 32 Bytes. Dies ist nicht das gleiche wie ein "file
of char". Die normale Eingabe und Ausgabe bezieht
sich auf Textfiles. In Textfiles können Sie Integer-Zahlen,
Zeichen, Datenfelder (Arrays) und Strings schreiben bzw.
lesen. Auch Boolean-Werte können geschrieben werden.
Enumerated 1 oder 2 Bytes, je nach Anzahl der Aufzählungen.
Wie bereits oben erwähnt, können Sie Arrays, Zeiger, Records und Dateien,
basierend auf den obengenannten Typen verwenden. Auch Synonymtypen sind
einsetzbar (wie "type int = integer;").
Beachten Sie ausserdem: Überall, wo Sie einen Typen benötigen, können Sie
eine vollständige Typenbeschreibung verwenden. Einige Compiler haben
hierbei Probleme, und ich bin mir auch nicht sicher, was das Standard-
Pascal in diesem Zusammenhang sagt, aber das ist mir eigentlich auch
ziemlich egal.
In Version 1.0 mußten mehrdimensionale Arrays ausgeschrieben werden. Es war
also nicht möglich, folgendes zu schreiben:
Array [0..5, 0..11] of Integer;
Stattdessen mußte folgende Konstruktion benutzt werden:
Array [0..5] of Array [0..11] of Integer;
für die Definition, und "ArrayName[x][y]" im Programm. Da die meisten
Pascal-Compiler die Abkürzung mit Komma erlauben, ist dies nun auch in
PCQ möglich.
5.3 Variablen (VAR)
-------------------
Variablen-Deklarationen werden wie im Standard-Pascal behandelt:
<Var Declaration> ::= Var <Var Definitions> ;
<Var Definitions> ::= <Var Definition> |
<Var Definition> ; <Var Definitions>
<Var Definition> ::= <Identifier List> : <Type Specification>
Globale Variablen werden wie statischer Speicher zugewiesen. Alle Variablen,
die innerhalb einer Prozedur oder Funktion deklariert werden, werden im Stack
zugewiesen. Wenn Ihr also re-entante Routinen schreibt, solltet Ihr globale
Variablen vermeiden.
Beachtet, daß lokale Variablen (nicht typed Konstanten) entfernt werden, wenn
die Funktion verlassen wird, wodurch diese nachher nicht mehr angesprochen
werden können (z.B. mit einer Zeigervariablen).
5.3.1 Standard-Variablen
Es gibt diverse Variablen, die in PCQ eingebaut sind. Diese sind immer vor-
handen und verhalten sich wie globale Variablen. Diese sind:
CommandLine : String;
Zeiger auf den Speicher der Kommandozeile, mit der der Benutzer das Programm
geladen hat (ohne Name des Programms und jeglichen Indirection-Parametern).
Falls das Programm von der Workbench gestartet wurde, ist dieser Wert nicht
definiert. Um sicher zu gehen, sollte diese Variable als "Read-only" behan-
delt werden. Macht eine Kopie davon, wenn sie modifiziert werden soll!
ExitProc : String;
Diese Variable zeigt auf die erste Prozedur, die am Programmende aufgerufen
wird, wenn ein Run-Time-Fehler auftritt. Siehe "Exit Procedures" für mehr
Infos.
ExitCode : Integer;
Falls das Programm normal beendet wurde, ist dies 0. Falls Exit()
aufgerufen wurde, ist es das Argument des Aufrufs, ansonsten die Nummer
eines Run-Time-Fehlers, der auch an AmigaDOS übergeben wird.
ExitAddr : Address;
Falls das Programm aufgrund eines Run-Time-Fehlers abgebrochen wurde,
enthält diese Variable die Adresse des Befehls nach dem Fehler.
HeapError : Address;
Enthält die Adresse einer Funktion, die aufgerufen werden so,, wenn New() o.
AllocString() keinen Speicherblock reservieren kann. Siehe die Sektion "Spei-
cher-Verwaltung".
Input : Text;
Es korrespondiert zum Standard-Eingabe-Kanal des Programms, wie z.B. dem CLI,
von dem es aufgerufen wurde. PCQ-Pascal-Programme erwarten eine Art von Stan-
dard-Inputfile, es sei denn, Ihr sagt ihm, das nicht zu tun. Siehe die Sek-
tion "Input/Output".
Output : Text;
Wie Input, nur zum Ausgabe-Kanal. Dies ist in den meisten Fällen der CLI,
von dem es gestartet wurde.
5.4 Prozeduren- und Funktionen-Deklarationen
--------------------------------------------
Prozeduren und Funktionen erlauben es, allgemeine Routinen mit einem größeren
Block zu definieren. Prozeduren werden durch Procedure-Statements aufgerufen
und Funktionen werden durch die Benutzung in Ausdrücken aufgerufen.
Das Format für Prozeduren- und Funktionen-Definitionen ist:
<Proc Declaration> ::= Procedure <Identifier> ; <Body> |
Procedure <Identifier>
( <Formal Params> ); <Body>
<Body> ::= <Block> | External | Forward
<Formal Params> ::= <Identifier List> : <Type Specification> |
var <Identifier List> : <Type Specification>
<Func Declaration> ::= Function <Identifier> :
<Type Specification> ; <Body> |
Function <Identifier> ( <Formal Params> ) :
<Type Specification> ; <Body>
Denkt aber daran, daß Prozeduren nur benutzt werden kann, wo <Statement>
steht (in anderen Struktur-Definitionen, Anm.d.Ü.), während Funktionen nur
bei <Expression> benutzt werden können.
5.4.1 Parameterübergabe
Pascal bietet 2 verschiedene Arten von Parameterübergabe.
Die erste Art nennt sich Wert-Parameter, was der normale Fall ist. Wenn ein
Parameter als Wert übergeben wird, macht der Compiler eine Kopie des Werts
und übergibt diesen der Funktion. Was auch immer die Prozedur oder Funktion
mit dem Wert macht, ist dem Hauptprogramm egal. Es wirkt sich also auf die
Original-Werte nicht aus. Beim Aufruf der Prozedur kann man jeden Ausdruck
(des korrekten Typs) als den aktuellen Parameter übergeben.
Die 2. Art sind Referenz-Parameter. Diese werden dadurch erkenntlich gemacht,
indem man dem Parameter das Wort VAR im Prozeduren- oder Funktions-Header
voranstellt. Die Prozedur oder Funktion arbeitet mit den übergebenen Refe-
renz-Parametern, d.h. diese Werte werden verändert und beim Rücksprung aus
der Prozedur/Funktion ans Programm zurückgegeben. D.h. die Variablen enthal-
ten nicht mehr dieselben Werte wie beim Prozedur/Funktions-Aufruf. Referenz-
Parameter müssen als Variablen-Referenzen übergeben werden und können keine
kompletten Ausdrücke sein. Referenzparameter müssen auch eine genauere Typen-
kompatibilität aufweisen als Wert-Parameter, denn eine 4-Byte-Integer-Variab-
le kann nicht in eine 1-Byte-Lücke geschrieben werden.
Man sollte also normalerweise Wert-Parameter bevorzugen (was Bugs reduziert),
ausgenommen von 3 Fällen:
1. der Wert einer Variable SOLL von einer Prozedur/Funktion verändert werden,
2. der Parameter ist groß (wenn ein Wert-Parameter verwendet wird, muß der
Compiler erst eine komplette Kopie im Stack davon machen, bevor die Rou-
tine aufgerufen wird, was Zeit und Stack-Speicher benögt, während der
Compiler bei einem Referenz-Parameter lediglich die Adresse in den Stack
bringen muß),
3. der Parameter ist ein File-Typ (dann MUSS der Parameter als Referenz über-
geben werden).
5.4.2 Vor-Definition/Referenz von Prozeduren und Funktionen
In Pascal muß jeder Identifier deklariert werden, bevor er benutzt werden
kann, was Probleme geben kann, wenn Prozeduren oder Funktionen gegenseitig
abhängig sind. Pascal erlaubt daher die vor-definition von Prozeduren und
Funktionen, die weiter unten im Sourcecode voll definiert werden.
Eine Vor-Referenz sieht aus wie eine normale Prozedur oder Funktion, hat aber
das reservierte Wort "Forward" direkt nach dem Header. Der Header enthält al-
le Informationen, die der Compiler benötigt, um eine Routine korrekt aufzu-
rufen. Nach dieser "Forward"-Deklaration, kann die Routine benutzt werden.
Wenn die Routine schließlich definiert wird, versichert sich der Compiler,
daß diese mit der früher gegebenen Definition übereinstimmt. D.h., daß die
Anzahl der Parameter und die Parametertypen identisch sein müssen. Bei Funk-
tionen muß auch der Ergebnistyp übereinstimmen.
Die eigentlichen Namen der Parameter können verschieden sein - nur deren An-
zahl, Ordnung und Typ werden verglichen.
Eine Vor-Referenz muß im selben Block aufgelöst werden, wie sie deklariert
wurde. Dieselbe Routine kann nicht mehr als 1 Mal "forward"-referenzt werden.
5.4.3 Externe Verweise (Referenzen)
Externe Referenzen sind ähnlich wie Forward-Referenzen - sie sind Deklaratio-
nen von Routinen, die nicht voll definiert sind.
Der Unterschied aber ist, daß Forward-Referenzen später im selben Programmfi-
le definiert werden müssen, während externe Referenzen überhaupt nicht im
Sourcefile definert werden.
Die Deklaration von externen Referenzen ist identisch zu den Forward-Referen-
zen, mit der Ausnahme, daß das reservierte Wort "External" anstatt von "For-
ward" benutzt wird.
Externe Referenzen dienen zu 2 Hauptzwecken. Zuerst einmal erlauben sie die
Benutzung von Prozeduren und Funktionen in externen Files. Alle Systemrouti-
nen sind in den Include-Files deklariert, die eigentlich in externen Files
definiert sind. Sie erlauben auch den Aufruf von Routinen, die in einer an-
deren Sprache compiliert wurden. Siehe auch die Sektion "Externe Files".
5.4.4 Prozeduren (PROCEDURE)
Es gibt diverse Standard-Prozeduren, die im PCQ-Pascal integriert sind. Dazu
gehören:
(zum Syntax der Funktionen wäre noch zu sagen, daß immer zunächst "procedure"
angegeben wird, dann der Name der Prozedur mit den Übergabeparametern in
Klammern. VAR bedeutet eine Referenz-Variable, fehlt VAR, handelt es sich um
eine Wert-Variable. Hinter dem Doppelpunkt steht der Typ, von dem die Varia-
ble sein muß. Zur Verdeutlichung nehmen wir mal den 1. Syntax: Im Programm-
text müßte dann z.B. Close(Datei) stehen, wobei Datei jeder Filetyp sein
kann)
procedure
Close(FileVariable : jeder Filetyp);
Dies entfernt die Filevariable und alle Systemresourcen, die dazu benutzt
wurden, um das File offenzuhalten. Siehe auch "Ein/Ausgabe"
procedure
Dec(v : jede Ordnungszahl(variable) oder Zeiger);
oder
Dec(v, Menge : Integer);
Dec (Kürzel für Decrement) zieht 1 (oder mehr) von einer Variablen ab.
"Dec(x,n)" entspricht "x:=x-n", ist aber leicht effizienter. Die Variable
muß entweder eine Ordnungszahl (wie Char, Byte, Integer, usw.) oder ein Zei-
ger sein. Der optionale Mengen-Parameter muß ein Integer-Typ sein - fehlt
dieser, wird der 1. Wert geschätzt...
Ist die Variable ein Ordnungszahl-Typ, wird der Wert normal abgezogen. Ist
die Variable jedoch ein Zeiger, multipliziert Dec() den Wert mit der Größe
des Typs, auf den der Zeiger zeigt, multipliziert, d.h. wenn man einen Zei-
ger p des Typs ^Integer hat, zeiht Dec(p) im Endeffekt 4 vom Zeiger ab.
procedure
Dispose(var x : Zeigervariable);
Dies gibt den zugeordneten Speicher zurück ans System. Siehe auch "Speicher-
verwaltung".
procedure
Exit;
oder
Exit(error : Integer);
Exit() unterbricht ein Programm vorzeitig und ist damit die einzig
akzeptable Methode, ein Programm zu verlassen. Dabei wird der gleiche
Vorgang wie beim normalen Abbruch eines Programms vollzogen und dann die
Fehlernummer, die Sie übergeben, ans AmigaDOS übergeben. Diese Routine
gibt den gesamten Speicher frei und schliesst die geöffneten Dateien. Die
Fehlernummer sollte Null sein, wenn das Programm korrekt abgeschlossen
wurde, 5 bei einer Warnung, 10 bei einem Fehler, und 20 bei einem
katastrophalen Fehler.
Exit ist die systemsichere Art ein Programm abzubrechen - auf die DOSExit-
Routine von AmigaDOS sollte möglichst nicht zurückgegriffen werden. Wenn man
Exit aufruft, werden alle Exit-Prozeduren in der Reihenfolge abgearbeitet.
Dazu gehört die Standard-Routine, die alle geöffneten Files schließt und den
Speicher und alles andere, was man definiert hat, freigibt.
procedure
Get(var FileVariable : FileTyp);
Dies bewegt den File-Zeiger auf das nächste Element des Files, ohne dabei ein
Element einzulesen. Das Element im Filepuffer kann mit dem FileVariable^ Syn-
tax erreicht werden. Siehe auch "Ein/Ausgabe".
procedure
Inc(VarReference : Ordnungszahl oder Zeiger);
oder
Inc(VarReference : Ordnungszahl oder Zeiger, Wert : Integer);
Inc() addiert einen Integer-Wert zur Variable VarReference. Wenn kein Wert
spezifiziert ist, wird 1 addiert. Falls die Variable ein Ordnungszahltyp ist,
wird die Addition normal ausgeführt. Ist sie allerdings ein Zeiger, wird der
Wert zuerst mit der Größe des Objekts, auf das der Zeiger zeigt, multipli-
ziert, d.h. wenn p ein Zeiger auf einen Integer-Typ ist, wird 4 zu p addiert
(die Größe eines Integers in Bytes).
procedure
New(var x : Zeigervariable);
Diese allokiert (d.h. reserviert) Speicher für den Platz des Variablentypes
und speichert die Adresse in x. Siehe auch "Speicherverwaltung".
procedure
Put(var FileVariable : bel. File-Typ);
Dies verschiebt den Filezeiger hinter dem aktuellen Element und schreibt das
aktuelle Element auf die Disk, wenn notwendig. Das aktuelle Element kann mit
Hilfe des FileVariable^ Syntax gesetzt werden. Siehe auch "Ein/Ausgabe".
procedure
Read(var FileVariable : Text oder File, Var-Referenzen...);
Dies liest Informationen aus einem AmigaDOS-File. Die Filevariable ist op-
tional und die Prozedur ist eine der ganz wenigen in Pascal, die eine variab-
le Anzahl an Argumenten hat. Siehe auch "Ein/Ausgabe".
procedure
Readln(var FileVariable : Text, Variablen-Referenzen...);
Dies ist identisch mit "Read", mit den folgenden Ausnahmen:
- es kann nur bei Textfiles benutzt werden,
- es kann nur benutzt werden, nachdem alle benötigten Argumente gelesen wur-
den,
- es liest alle Buchstaben bis zum Beginn der nächsten Zeile.
Siehe auch "Ein/Ausgabe".
procedure
Reset(var FileVar : File oder Text;
FileName : String;
BufferSize : Integer);
Dies öffnet ein File zum Lesen. Der BufferSize-Parameter ist optional.
Wenn Ihr die {$I-} Direktive zum manuellen IO-Checken benutzt habt, solltet
Ihr IOResult überprüfen, um das File korrekt geöffnet wurde. Wenn Ihr aber
automatisches IO-Überprüfen benutzt, bricht das Programm mit einem Runtime-
Error ab, wenn das File nicht korrekt geöffnet wird. Siehe auch "Ein/Ausga-
be".
procedure
Rewrite(var FileVar : File oder Text;
FileName : String;
BufferSize : Integer);
Dies öffnet ein File zur Ausgabe, indem es alle existierenden Files desselben
Namens löscht. Das BufferSize-Argument ist optional. Falls das File nicht ge-
öffnet werden kann, liefert IOResult einen Wert ungleich 0. Wenn Ihr automa-
tisches IO-Checken benutzt, bricht das Programm mit einem Runtime-Error ab.
Siehe auch "Ein/Ausgabe".
procedure
Trap(num : Integer);
Diese Prozedur kann von versch. Debuggern als eine Art automatische Abbrech-
marke benutzt werden. Wenn Ihr also die korrekte "Trap" (zu deutsch: Falle)
benutzt, könnte der Debugger hier stoppen - oder auch nicht :-) .
procedure
write(FileVariable : Text oder File, Ausdrücke...);
Dies gibt Informationen in einem AmigaDOS-File aus. Die Filevariable ist op-
tional; es können eine variable Anzahl Argumente übergeben werden. Siehe auch
"Ein/Ausgabe".
(im Grunde genommen ist "write" das Gegenstück zu "read" - Anm.d.Ü.)
procedure
writeln(FileVariable : Text, Ausdrücke...);
Writeln ist identisch zu write, mit den folgenden Ausnahmen:
- es kann nur bei Textfiles benutzt werden,
- am Ende der Argumente, die von Writeln ausgegeben werden, wird die Zeile
beendigt.
Siehe auch "Ein/Ausgabe".
5.4.5 Funktionen (FUNCTION)
Es gibt ein paar in dem Pascal-Compiler eingebaute Funktionen. Diese wären:
(zum Syntax der Funktionen wäre noch zu sagen, daß zunächst "function", dann
der Funktionsname mit Übergabeparametern in Klammern, gefolgt von einem
Doppelpunkt und dem Funktionstyp angegeben ist. VAR x bedeutet dabei eine
Referenz-Variable, x eine Wert-Variable. Hinter dem Doppelpunkt steht, von
welchem Typ die Variable sein muß. Nehmen wir zur Verdeutlichung das 1. Bei-
spiel. Hier könnte z.B. im Programm x:=adr(var y) stehen. Das würde die Ad-
resse der Variablen y in x speichern. Dabei muß x vom Typ Adresse sein, wäh-
rend der Typ von y hier egal ist. Anm.d.Ü.)
function
adr(var x : jede Variable): Address;
übergibt die Adresse von x (entspricht Addr() in Turbo); es kann
auch der Operator @ benutzt werden.
function
abs(x : numerischer Typ) : der gleiche Typ;
Übergibt den absoluten Wert (d.h. den Wert der Variablen ohne Vor-
zeichen).
function
arctan(x : numerischer Typ) : real;
übergibt den ungefähren Arctangens des Parameter in Radianten.
function
Bit(t : Integer) : Integer;
übergibt die Zahl, die der Bit-Position entspricht.
function
chr(x : numerischer Typ) : char;
verwandelt jeden numerischen Typ in den zugehörigen ASCII-Charakter.
(z.B. writeln(chr(65)) liefert "A" - Anm.d.Ü.)
function
ceil(x : real): real;
übergibt die kleinste Ganzzahl größer oder gleich x.
function
cos(x : numerischer Typ in Radianten) : real;
übergibt den Cosinus dew angegebenen Winkels in Radianten.
function
eof(x : jedes File) : boolean;
Übergibt true, falls Sie sich am Ende einer Input-Datei
befinden. Ansonsten wird false übergeben. Siehe auch "Ein/Ausgabe".
function
exp(x : numerischer Typ) : real;
übergibt e hoch x, wobei e rund 2,71828 ist.
function
float(x : integer, short oder byte) : real;
verandelt x in eine Fließkommazahl. Falls die Integer-Zahl ziemlich
groß ist, wird sie evtl. gerundet. Somit ist "Trunc(Float(IntVar)) =
IntVar" nicht immer richtig (trunc ist die "Gegenfunktion zu float,
Anm.d.Ü.).
function
floor(x : real): real;
übergibt die größte Ganzzahl kleiner oder gleich x.
function
IOResult : Integer;
überprüft, ob seit der letzten Überprüfung Fehler aufgetreten sind.
Normalerweise fügt PCQ-Pascal Statements ein, um das IO-Ergebnis au-
tomatisch zu prüfen, was man aber mittels der {$I-} Direktiven ab-#
schalten kann. Danach muß man dieses aber selbst überprüfen. Das Er-
gebnis wird bei der Überprüfung gelöscht. Wenn es also weiterbenutzt
werden soll, muß es in einer Variablen gespeichert werden. S.a. der
Abschnitt "Ein/Ausgabe".
function
ln(x : numerischer Typ) : real;
übergibt den natürlichen Logarithmus (z.B. zur Basis e) des Parame-
ters.
function
odd(x : numerischer Typ) : boolean;
Übergibt true, falls x ungerade ist, und ansonsten false.
function
open(fname : String;
var fvar : File oder Text;
BufferSize : Integer) : Boolean
diese Funktion entspricht der Rewrite-Prozedur. Sie öffnet ein File
zur Ausgabe und löscht dabei jedes existierende File mit demselben
Namen. Falls das File korrekt geöffnet werden kann, übergibt Open
den Wert true. Falls nicht, false. Der BufferSize-Parameter ist op-
tional. Siehe auch "Ein/Augabe".
function
ord(x : jeder Ordinaltyp): integer;
Übergibt den ordinalen Wert des Arguments als Integer-Zahl, d.h.
Chars, Booleans usw. werden in ihre numerischen Equivalente verwan-
delt.
function
pred(x : ordinaler Typ) : der gleiche Typ;
Übergibt die nächstkleinere Ordnungszahl vom selben Typ. So ist z.B.
Pred(2) 1. Falls das Argument der kleinste Wert desselben Typs ist
(wie z.B. Pred(a), wenn a=0 und eine Byte-Variable ist), ist sein
Verhalten undefiniert.
function
ReOpen(fname : String;
var fvar : File oder Text;
buffersize: Integer) : Boolean
Funktionsform von Reset. Öffnet ein File zum Lesen und gibt True
zurück, falls alles ok ist. Wenn nicht, wird False zurückgegeben.
Der BufferSize-Parameter ist optional und kann jeden Wert besitzen.
Siehe auch "Ein/Ausgabe".
function
round(x: real) : integer;
rundet einen reellen Wert zum nächsten Integer. Es ist im Grunde ge-
nommen Trunc(Wert+0.5). Daher läuft diese Funktion etwas langsamer
ab als die Trunc-Funktion.
function
sin(x: numerischer Typ in Radianten) : real;
berechnet den Sinus für einen gegebenen Winkel gemessen in Radian-
ten.
function
SizeOf(t : Typenidentifier) : Integer;
übergibt die Größe des spezifierten Typen. Beachtet, daß der Typ als
einzelner Identifier spezifiziert werden muß.
function
sqr(x : numerischer Typ) : selber Typ;
berechnet x*x (x²).
function
sqrt(x : numerischer Typ) : real;
berechnet die Quadratwurzel von x, d.h. Sqrt(x) * Sqrt(x) = x.
function
succ(x : ordinaler Typ) : der gleiche Typ;
übergibt die nächstgrößere Ordnungszahl desselben Typs. Falls dieser
Wert nicht definiert ist (z.B. Succ(n), wenn n der größte Wert eines
Typs ist), ist die Funktion nicht definiert.
function
tan(x : numerischer Typ in Radianten) : real;
übergibt den Tangens des Winkels gemessen in Radianten. Falls der Pa-
rameter ein ungerades Vielfaches von Pi/2 ist, ergibt diese Funktion
bedeutungslose Werte (die Tangens-Funktion ist an ungeraden Vielfa-
chen von Pi/2 undefiniert!).
function
trunc(x : real) : integer;
übergibt den Ganzzahlanzeil einer Fließkommazahl. Dies ist der
schnellste Weg, eine reelle Zahl in eine Integer-Zahl zu verwandeln.
6. Namen (Label)-Deklarationen
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Namen müssen in der Deklarationen-Sektion deklariert werden, bevor sie im
Programm benutzt werden können. Die Namen-Deklaration hat die folgende Form:
<Label Declarations> ::= Label <Identifiers> ;
<Identifiers> ::= <Identifier> | <Identifier> , <Identifiers>
Beachtet, daß Standard- und Turbo-Pascal für Labels auch numerische Werte er-
laubt, während PCQ-Pascal lediglich normale Identifier erlaubt. Wenn Ihr also
ein Programm portiert habt, müßt Ihr vor den Zahlen einen (oder mehrere)
Buchstaben vor die Zahlen platzieren, um die Identifier zu legalen zu machen.
7. Spezielle Befehle
^^^^^^^^^^^^^^^^^^^^
Zunächst unterstützt der PCQ If-, While-, Repeat-, For-, Case-,
Goto- und With- Befehle.
Die If-, While- und Repeat-Befehle arbeiten ziemlich so, wie sie
sollten. Der Case-Befehl entspricht jetzt mehr dem Standard-Pascal als in
V1.0. Hier einige Beispielausdrücke:
case Letter of case Number * 5 of
'a' : statement1; -MaxInt..0 : statement1;
'b'..'g' : statement2; 1..MaxInt : statement2;
'j', end;
'm'..'o',
'h' : statement3;
else
statement4;
end;
Der For-Befehl unterstÜtzt "downto", was das Inkrement von 1 auf -1 ändert.
Er unterstÜtzt auch "by" und erlaubt Ihnen damit, die Schrittweite
festzulegen. Das Argument des "by"-Teils kann jeder reguläre Ausdruck sein,
fÜr negative Schrittweitn mÜssen Sie aber "downto" statt "to" benÜtzen,
oder die Schleife wird nur einmal ausgefÜhrt. Nebenbei bemerkt laufen
For-Schleifen immer mindestens einmal ab. In jedem Fall sieht die Syntax
ungefähr folgendermassen aus:
for <Variable> := <Ausdruck> to|downto <Ausdruck>
[by <Ausdruck>] do <Befehl>;
Der letzte Befehl ist "return", der einfach eine PROCEDURE frühzeitig
abbricht. Eine FUNCTION können Sie vorzeitig abbrechen, indem Sie den
Funktionsnamen einem Wert zuordnen; "return" funktioniert also nur bei
Prozeduren.
8. Ausdrücke
^^^^^^^^^^^^
Der Compiler akzeptiert den normalen Ausdrucks-Syntax, wie die meisten
anderen Pascal-Dialekte.
Folgende Konstanten werden dabei von PCQ akzeptiert:
(digit heißt Ziffer, binary = binär, hex = hexadezimal, ASCII char = ASCII-
Zeichen, char value = Zeichen-Wert, Const = Konstante - Anm.d.Ü.)
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<digits> ::= <digit> | <digit> <digits>
<binary digit> ::= 0 | 1
<binary digits> ::= <binary digit> |
<binary digit> <binary digits>
<hex digit> ::= <digit> | a | b | c | d | e | f
<hex digits> ::= <hex digit> | <hex digit> <hex digits>
<Float Const> ::= <digits> . <digits>
<Integer Const> ::= <digits> | $ <hex digits> | % <binary digits>
<Char Value> ::= <ASCII char> | \ <escape sequence>
<Char Values> ::= <Char Value> | <Char Value> <Char Values>
<Number Const> ::= <Integer Const> | <Float Const>
<Char Const> ::= ' <Char Value> '
<String Const> ::= " <Char Values> "
<Char Array Const> ::= ' <Char Values> '
Das mag vielleicht etwas verwirrend sein, daher hier eine nähere Erklärung.
Es heißt, daß immer wenn der Compiler ein "Integer" erwartet, kann man zwi-
schen dezimaler, hexadezimaler und binärer Darstellung wählen. Außerdem müs-
sen Fließkommakonstanten nur in der normalen 23.23 Form erkannt werden - ohne
Exponenten. Schließlich können auch Escape-Sequenzen in der Art von C für je-
de Textkonstante benutzt werden. Diese Escape-Werte werden in der "Strings"-
Sektion (Zeichenketten) beschrieben.
Wie sind nun diese Sachen in komplette Ausdrücke gegliedert?
Ok, hier sind die Definitionen:
(expression heißt Ausdruck, simple = einfach, rel op = Vergleichsoperanden,
add op = Additionsoperanden, mul op = Multiplikationsoperanden, factor =
Faktor, number const = numerische Konstante, Char Const = Zeichenkonstante,
Sting Const = Stringkonstante, Array = Feld, Variable Reference = Referenz-
Variable, Function call = Funktionsaufruf)
<expression> ::= <simple> <rel op> <simple> | <simple>
<rel op> ::= = | <> | < | > | <= | >=
<simple> ::= <term> <add op> <term> | <term>
<add op> ::= + | - | or | xor
<term> ::= <factor> <mul op> <factor> | <factor>
<mul op> ::= * | / | div | mod | and | shr | shl
<factor> ::= <Number Const> |
<Sign> <Number Const> |
<Char Const> |
<String Const> |
<Char Array Const> |
<Variable Reference> |
@ <Variable Reference> |
not <factor> |
( <Expression> ) |
<Function call>
Alles klar? - Nein?! - Ok, das alles heißt, daß Ausdrücke aus Faktoren auf-
gebaut sind, die Grundblöcke eines Ausdrucks. Ein Faktor kann dabei eine Kon-
stante oder Variable, ein Ausdruck in Klammern und alle anderen oben aufge-
führten Sachen sein. Die folgenden Beispiele sind alles Faktoren:
23
(4.0 - 2.3)
%01000001
'a'
RecordPtr^.Field1[45,23]
@Var1
Abs(Var2)
Faktoren können auch Operatoren haben, die sich nur für 1 Wert verwenden las-
sen (im Gegensatz zu binären Operatoren, die für 2 verwendbar sind). Diese
sind:
@ Operator für Adresse-von. Übergibt die Adresse der Referenz-Variablen
direkt danach. Entspricht der Adr() Funktion.
- Negation. Nimmt den negativen Wert des folgenden Faktors.
+ Bewirkt im Prinzip nichts.
not Boolean oder bitweise Negation. Boolean-Faktoren werden zum Gegenteil
und Integer-Faktoren werden bitweise umgedreht.
Diese Faktoren werden in Terme eingebaut. Terme sind entweder dasselbe wie
Faktoren oder 2 Faktoren mit einem Multiplikationsoperator dazwischen. Die
Multiplikationsoperatoren sind folgende:
* Multiplikation (mal).
/ Fließkommadivision (geteilt). Beide Argumente werden vor der Durch-
führung der Berechnung in Fließkommavariablen konvertiert.
div Integer-Division. Beide Argumente dieses Operators werden zuerst in
Integer verwandelt.
mod berechnet den Rest. Beide Argumente werden zuerst nach Integer kon-
vertiert (wenn nötig).
and logische AND-Funktion.
shl schiebt das linke Argument um n Bitpositionen nach links (1 shl
5 = 32; 32 ist binär 100000 - Anm.d.Ü.)
shr Schiebt nach rechts. Es wird logisch, nicht aritmethisch verschoben,
so ergeben negative Werte positve Resultate. Bei diesen Werten muß,
um ein vernünftiges Ergebnis zu erhalten, auf "div" zurückgegriffen
werden.
Terme sind in einfache Ausdrücke eingebaut. Einfache Ausdrücke sind entweder
dasselbe wie Terme oder 2 Terme getrennt mit einem Additionsoperator. Diese
wären:
+ addiert die beiden Operanden.
- subtrahiert die beiden Operanden.
or logisches OR (oder).
xor exklusives OR (oder).
Diese einfachen Ausdrücke werden in kompletten Ausdrücken eingebaut.
Ausdrücke sind entweder dasselbe wie einfache Ausdrücke oder 2 einfache Aus-
drücke getrennt mit einem relativen Operator. Diese Operatoren produzieren
Boolean-Ergebnisse, und werden wie folgt definiert.
= übergibt TRUE falls die beiden Argumente gleich sind.
<> übergibt TRUE falls die beiden Argumente ungleich sind.
> übergibt TRUE falls das linke Argument größer als das rechte Argument
ist.
< übergibt TRUE falls das linke Argument kleiner als das rechte Argu-
ment ist.
>= übergibt TRUE falls das linke Argument größer oder gleich dem rechten
Argument ist
<= übergibt TRUE falls das linke Argument kleiner oder gleich dem rech-
ten Argument ist.
8.1 Berechnungsreihenfolge
--------------------------
Die normalen mathemat. Regeln werden von PCQ auch so behandelt. Die einfachen
(unary) Operatoren werden zuerst berechnet, dann kommen alle multiplikativen
Operationen, danach die additiven Operationen und schließlich die relativen
Operationen. Die Vorrangigkeit sieht somit wie folgt aus:
Operator Vorrangigkeit
-------------------------------------
@,not,unary +/- erste (höchste)
*,/,div,mod,
and,shr,shl zweite
+,-,or,xor dritte
=,<>,<,>,>=,<= vierte (letzte)
Alle Ausdrücke in Klammern werden natürlich zuerst berechnet. 2 oder mehr
Operatoren des gleichen Ranges werden von links nach rechts berechnet (z.B.
"x+y+z" wird als "(x+y)+z" berechnet).
(Es sollten hier vielleicht noch ein paar weitere Rechenbeispiele gegeben
werden: bei "x+y*z" wird zuerst y*z berechnet und dann x addiert. Das kann
mittels (x+y)*z verhindert werden; für Leute, die sich damit auskennen,
war dies zwar unnötig - aber es gibt halt auch noch andere... - Anm.d.Ü.)
8.2 Konstanten-Ausdrücke
------------------------
Konstantenausdrücke sind dasselbe wie normale Ausdrücke; sie müssen lediglich
beim Compilieren komplett berechnet werden. Daher können sie keine externen
Funktionen oder Reference-Variablen benutzen. Benutzt werden können aller-
dings alle Operatoren wie in Standard-Funktionen.
8.3 Referenz-Variablen
----------------------
Referenz-Variablen können schrecklich komplex werden. Der Grund-Syntax sieht
wie folgt aus:
(Variable Reference = Referenzvariable, Field = Feld, der Rest dürfte wohl
klar sein - Anm.d.Ü.).
<Variable Reference> ::= <Variable Mark> |
<Variable Mark> <Selectors>
<Variable Mark> ::= <Variable ID> |
<Type ID> ( <Variable Reference> )
<Selectors> ::= <Selector> | <Selector> <Selectors>
<Selector> ::= ^ | .<Field Id> | [ <Index Expressions> ]
Eine Referenz-Variable besteht also aus dem Identifier einer globalen oder
lokalen Variable, gefolgt von einer beliebigen Anzahl von Selektoren in allen
möglichen Kombinationen. Die andere Möglichkeit, die sich etwas von der V1.2
des Compilers unterscheidet, ist, daß Ihr ein "Type-Cast" irgendwo in der
Mitte von alledem platzieren könnt. Die folgenden Variablen sind alle Refe-
renz-Variablen:
Var1
Var1^
Var1^.Field1[34,56]
Type2(Var1)^.Field2^^[23]
Wenn Ihr damit anfangt, Programmcode wie diesen zu schreiben, solltet Ihr
einmal Pause machen...
9. Statements
^^^^^^^^^^^^^
Statements sind das Fleisch und die Kartoffeln der Programme - sie sind das,
was zwischen BEGIN und END. steht (also das Programm an sich - Anm.d.Ü.).
Sie haben die folgende Form:
<Statement> ::= <If Statement> |
<While Statement> |
<Repeat Statement> |
<For Statement> |
<Case Statement> |
<With Statement> |
<Compound Statement> |
<Assignment Statement> |
<Procedure Call> |
<Return Statement> |
<Goto Statement>
(Es gibt keine allgemeine Übersetzung für Statement; Anweisung kommt dem ei-
gentlichen Namen aber schon recht nahe (wobei Statements aber auch Befehle
u.ä. sein können. Alle oben aufgeführten Statements werden aber im Folgenden
erklärt. - Anm.d.Ü.)
9.1 IF-Anweisung
----------------
(deutsch: wenn ... dann ...)
Die IF-Anweisung erlaubt das Testen verschiedener Alternativen. Es hat die
folgende Form:
<If Statement> ::= if <Boolean Expr> then <Statement> |
if <Boolean Expr> then <Statement>
else <Statement>
Wenn der boolean Ausdruck TRUE ergibt, wird das Statement nach "then" ausge-
führt. Ergibt er aber FALSE, wird das Statement nach "else" ausgeführt, so-
fern eines existiert.
Hier gibt´s eine Mehrdeutigkeit. Seht Euch einmal das folgende Fragment an:
if Expr then if Expr then Statement1 else Statement2
Zu welchem "if" gehört nun das "else"? - PCQ nimmt hier (wie die meisten Im-
plementationen) - das letztere "if".
(Sollen mehrere Anweisungen nach einem "then" abhängig vom "if"-Resultat aus-
geführt werden, so muß nach dem "then" ein "begin" und am Ende des in Abhän-
gigkeit auszuführenden Teils ein "end" stehen. Das ist auch bei anderen An-
weisungen, wie WHILE (s.u.) möglich - Anm.d.Ü.)
9.2 WHILE-Anweisung
-------------------
(deutsch: solange wie ... mache ...)
WHILE ist eine von mehreren Anweisungen, die Programmschleifen erlauben (die-
se Programmschleifen können abhängig von der Bedingung mehrfach durchlaufen
werden - Anm.d.Ü.).
Diese Anweisung hat folgende Form:
<While Statement> ::= while <Boolean Expr> do <Statement>
Beim Beginn der Schleife wird der boolean Ausdruck berechnet.
Falls das Ergebnis TRUE ist, wird der Teil des Statements ausgeführt. Ist das
abgeschlossen, wird der Ausdruck neu berechnet. Dieser Vorgang wird solange
durchgeführt, bis der Ausdruck FALSE ergibt. Sobald das passiert, wird nach
der WHILE-Schleife fortgefahren.
("while <Boolean Expr> do begin <Statement> <Statement> ... end" funktioniert
auch hier - Anm.d.Ü.)
9.3 REPEAT-Anweisung
--------------------
(deutsch: wiederhole ...; bis ...)
Die "Repeat"-Schleife bietet eine leicht unterschiedliche Variante d. Schlei-
fen. Es hat die folgende Form:
<Repeat Statement> ::= repeat <Statements> until <Boolean Expr>
<Statements> ::= <Statement> | <Statement> ; <Statements>
Die nach der "Repeat"-Anweisung folgenden Anweisungen werden beim 1. Schlei-
fendurchlauf immer ausgeführt. Erst danach wird der boolean Ausdruck berech-
net. Ergibt der Ausdruck FALSE, wird der Anweisungen-Block nochmals durchlau-
fen. Ergibt er TRUE, wird mit dem Statement nach "until" fortgefahren.
(begin/end sind auch bei mehreren Statements NICHT nötig - Anm.d.Ü.)
9.4 FOR-Anweisung
-----------------
(deutsch: von ... bis ... mache ...)
Die FOR-Anweisung stellt eine 3. Art von Schleifen dar. Sie ist für Zwecke
geeignet, in denen man genau weiß, wie oft die Schleife durchlaufen werden
soll. Der Syntax ist der folgende:
(Direction = Richtung (aufwärts oder rückwärts zählen) - Anm.d.Ü.)
<For Statement> ::= for <Variable Reference> :=
<Expression> <Direction>
<Expression> do <Statement>
<Direction> ::= to | downto
Das unterscheidet sich von PCQ V1.1. Bei einer "for"-Schleife wir der 1. Aus-
druck (der Anfangswert) berechnet und in der Referenz-Variablen (die im Index
aufgerufen wird) gespeichert. Dann wird der Index verglichen mit dem 2. Aus-
druck (dem Zielwert). Falls die Richtung "to" (aufwärts) und der Index klei-
ner oder gleich dem Zielwert ist, wird der Statement-Teil ausgeführt. Danach
wird der Index um 1 erhöht und erneut mit dem Zielwert verglichen. Das geht
dann solange weiter, bis der Index größer als der Zielwert ist. Ist das der
Fall, wird nach der "next"-Anweisung fortgesetzt.
Ist die Richtung "downto", passiert im Prinzip dasselbe, abgesehen davon, daß
der Index bei jedem Durchlauf um 1 verringert wird. Die Schleife wird dann
solange durchlaufen, bis der Index kleiner als der Zielwert ist.
Beachtet, daß die "for"-Schleife möglicherweise nicht ausgeführt wird, wenn
der Anfangswert größer als der Zielwert ist (für "to"-Loops oder im umgekehr-
ten Fall bei "downto"-Schleifen).
Beachtet außerdem, daß der Zielwert bei jedem Schleifendurchlauf vollkommen
neu berechnet wird. Wenn es also ein komplexer Ausdruck ist, solltet Ihr ihn
wohl besser vor der "for"-Anweisung in einer Variable zwischenspeichern.
Ihr könnt immer eine "While"- oder "Repeat"-Schleife anstatt einer "For"-
Schleife benutzen. Tatsächlich wird die "For"-Schleife auch am wenigsten be-
nutzt.
9.5 CASE-Anweisung
------------------
(deutsch: falls ... aus ... (dann) ...)
Die CASE-Anweisung wird dazu benutzt, einen Wert aus verschiedenen möglichen
Alternativen zu testen, und die Anweisungen in Abhängigkeit zum Ergebnis aus-
zuführen. Folgender Syntax:
(case vals = mögliche Werte - Anm.d.Ü.)
<Case Statement> ::= case <Expression> of <Alternatives> end |
case <Expression> of <Alternatives>
else <Statement> end
<Alternatives> ::= <Alternative> | <Alternative> ; <Alternatives>
<Alternative> ::= <Case Vals> : <Statement>
<Case Vals> ::= <Case Val> | <Case Val> , <Case Vals>
<Case Val> ::= <Const Expr> | <Const Expr> .. <Const Expr>
Bei der Ausführung der "case"-Anweisung wird der Ausdruck berechnet. Er wird
dann mit allen Fällen verglichen. Sobald ein Fall erfüllt ist, wird das fol-
gende Statement ausgeführt. Ist keiner der angegebenen Fällen eingetreten,
wird das Statement nach "else" ausgeführt, sofern eines existiert. Falls
nicht, wird mit dem Statement nach der "Case"-Schleife fortgefahren.
Beachtet, daß der Ausdruck nur 1 Mal berechnet wird, wodurch eine CASE-Anwei-
sung effizienter wie eine Serie aus IF-Anweisungen sein kann.
("case ... of ... : begin ... end" ist möglich, um mehr als 1 Anweisung aus-
führen zu lassen - Anm.d.Ü.)
9.6 WITH-Anweisung
------------------
Die WITH-Anweisung erlaubt das Spezifizieren einer Record-Variable, auf die
sich ein Statement bezieht. Innerhalb des Statements kann man Referenzen auf
die Felder des Records abkürzen, indem man nur den Feldnamen angibt. Das
spart etwas Tipparbeit und der Compiler generiert etwas effizienteren Code.
Nun zum Syntax:
<With Statement> ::= with <Expressions> do <Statement>
<Expressions> ::= <Expression> | <Expression> , <Expressions>
Wenn Ihr mehr als 1 Record (getrennt von Kommas) angebt, behandelt der Compi-
ler diese wie in Statements genistet, mit dem 1. Record im äußersten Bereich
und dem letzten im innersten.
Im Statement werden alle Referenzen zu Feldern als sich auf den innersten Re-
cord beziehend angenommen. Mehrdeutigkeiten können durch die Benutzung von
Spezifikationen des gesamten Records geklärt werden.
WITH-Anweisungen sind speziell dazu nützlich, wenn ein Referenz-Record lang
ist - er wird nur 1 Mal ausgeführt, was die Effizient erhöht. Sie ist auch
dann speziell nützlich, wenn es darum geht, Werte in einem Record zu platzie-
ren. Sie verbessert zwar nicht immer die Effizienz des Programms, spart aber
viel Tipparbeit.
Wenn Ihr ein Statement der Form "with ^p do ..." habt, ist es wahrscheinlich
ein Fehler, den Wert von p zu ändern. Z.B. ist es ein Fehler, den Speicher
für den Record einer "With"-Anweisung freizugeben. Dieser Fehler wird vom PCQ
zwar nicht entdeckt, es wäre aber gut, ihn zu vermeiden.
9.7 COMPOUND-Anweisung
----------------------
Die COMPOUND-Anweisung erlaubt die Benutzung einer Reihe von Statements in
jeder Situation, in der zumindest 1 Statement (davon) aufgerufen wird. Der
Syntax ist der Folgende:
<Compound Statement> ::= begin <Statements> end
Ein "Compound"-Statement kann überall dort benutzt werden, wo ein Statement
erlaubt ist und der Statement selbst generiert keinen Extra-Programmcode.
9.8 ASSIGNMENT-Anweisung
------------------------
Assignment-Anweisungen werden dazu benutzt, um den Wert von Variablen zu set-
zen. Sie haben die folgende Form:
<Assignment Statement> ::= <Variable Reference> := <Expression>
Beachtet, daß der Typ der Variable mit dem Typ des Ausdrucks übereinstimmen
muß (siehe "Typendeklarationen" für spezielle Regeln)
9.9 PROCEDURE-Aufrufe
---------------------
Prozedur-Aufrufe transferieren die Ausführung zu einer vorher definierten
Routine und übergeben die benötigten Parameter.
Dabei gilt der folgende Syntax:
(Procedure Call = Prozeduraufruf, Actual Params = übergebene Parameter,
Expression = Ausdruck - Anm.d.Ü.)
<Procedure Call> ::= <Identifier> |
<Identifier> ( <Actual Params> )
<Actual Params> ::= <Actual Param> |
<Actual Param> ; <Actual Params>
<Actual Param> ::= <Expression>
Prozeduren unterscheiden sich dadurch von Funktionen, daß Funktionen einen
Wert zurückgeben und nur von Ausdrücken aufgerufen werden können, während
Prozeduren keinen Wert zurückgeben und nur von Statements aufgerufen werden
können.
Wenn eine Prozedur ausgeführt wird, wird jeder übergebene Parameter berechnet
und in den Stack geschrieben (von links nach rechts - im Gegensatz zu C!).
9.10 RETURN-Befehl
------------------
Normalerweise wird eine Prozedur bis zum Ende abgearbeitet und anschließend
mit dem Befehl nach dem Prozedur-Aufruf fortgefahren. Der "Return"-Befehl er-
laubt das sofortige Verlassen der aktuellen Prozedur. Er hat folgende einfa-
che Form:
<Return Statement> ::= return
Der "Return"-Befehl ist nur innerhalb von Prozeduren erlaubt. Er darf nicht
innerhalb von Funktionen verwendet werden, da diese einen Wert zurückgeben
müssen. Der analoge Befehl in einer Funktion ist ein Assignment auf den Iden-
tifier der Funktion. Der "Return"-Befehl ist auch nicht im Hauptprogramm zu-
lässig, weil das Programm somit abbrechen würde. Diesen Zweck erfüllt der
"Exit"-Befehl.
9.11 GOTO-Anweisung
-------------------
Nach der GOTO-Anweisung wird mit der Ausführung nach der angegebenen Sprung-
marke fortgefahren. Dies sollte man mit extremer Vorsicht verwenden und, wenn
möglich, vermeiden. Hier die Form:
<Goto Statement> ::= goto <Identifier>
Standard-Pascal erwartet, daß eine Goto-Sprungmarke aus einer Ziffern-Folge
besteht, und Turbo Pascal erweitert diese Definition, indem es auch Identi-
fier in Sprungmarken (Labels) erlaubt. PCQ-Pascal hingegen macht schon den
nächsten logischen Schritt, indem es Identifier als Sprungmarken erwartet -
und keine Nummern!
Die meisten Programmierer sind der Ansicht, daß das Arbeiten mit GOTO einfach
nur schlechte Programmierung ist, weil es das Verstehen und Debuggen von Pro-
grammen erschwert. Es gibt jedoch auch andere Probleme. Wenn man z.B. "goto"
innerhalb einer "for"-Schleife nach außen verwendet, wird der Stack des Pro-
gramms durcheinandergebracht. Ähnliche Sachen passieren bei der "With"-Anwei-
sung, ganz zu schweigen davon, was passiert, wenn man vom außerhalb in eine
"for"-Schleife springt...
Die Moral der Geschichte ist: versucht sie zu vermeiden!
10. Ein- und Ausgabe
^^^^^^^^^^^^^^^^^^^^
Es gibt einige Routinen im PCQ, die die Ein- und Ausgabe betreffen. Sie die-
nen als Interface mit AmigaDOS-Routinen. Die Pascal-Ein/Ausgabe-Routinen be-
nutzen alle AmigaDOS zum Lesen und Schreiben.
Pascal-Ein/Ausgabe hat jedoch verschiedene Vorteile. Zunächst einmal werden
alle PCQ-Files gepuffert, d.h. daß nicht jedes Mal zum Lesen/Schreiben auf
AmigaDOS zurückgegriffen werden muß. Das erhöht die Geschwindigkeit etwas.
Der andere Vorteil ist die Benutzung der Pascal-Routinen Read, ReadLn, Write
und Writeln.
Wenn man ein File im Programm entweder als "File of Some Type" (File eines
bestimmten Typs) oder als "Text" deklarieren, allocatet man im Endeffekt ei-
nen Record.
Benutzt man also so etwas wie "var filevar : file of integer", erzeugt man in
Wirklichkeit soetwas wie einen Record, der so aussehen würde (wenn seine Fel-
der direkt ansprechbar wären):
file = record
HANDLE : Ein AmigaDOS File-Handle
NEXT : Ein Zeiger auf das nächste offene Pascal-File
BUFFER : die Adresse des Filepuffers
CURRENT : die Stelle des Puffers des nächsten Read/Write
LAST : die letzte Position eines Read
MAX : die Adresse des Puffer-Endes +1
RECSIZE : die Größe der File-Elemente
INTERACTIVE : ein boolean Wert - TRUE heißt, daß das File ans CLI ge-
bunden ist
EOF : ein boolean Wert
ACCESS : entweder MODE_NEWFILE oder MODE_OLDFILE
end;
10.1 Text- und Typen-Files
--------------------------
Pascal hat 2 verschiedene Filetypen. Der normale Filetyp wird "typed File"
genannt und wird in folgender Weise deklatiert:
FileVar : File of ElementType
Typed Files sind im Grunde genommen unbegrenzte Sequenzen von Elementen von
diesem einen Element-Typ. Der Write-Befehl kann benutzt werden, um individu-
elle Elemente ans Ende des Files zu schreiben, und der Read-Befehl dazu, um
das aktuelle Element einzulesen und den File-Zeiger zum nächsten Element zu
bewegen. Ihr könnt nur einen Elementtyp in jedes "typed File" schreiben.
Typed Files speichern Ihre Elemente in genau demselben Format, wie sie auch
im Speicher auftreten, wodurch sie oft unlesbar sind. Ein File von Zeichen-
elementen ist zwar lesbar, aber ein File von Integer-Elementen würde unver-
ständlich sein. Da "typed Files" immer komplette Elemente speichern, hat je-
des Element notwendigerweise eine feste Größe.
Ihr braucht also eigentlich nichts anderes als "typed Files" - alles was Ihr
mit Pascal machen könnt, könnt Ihr damit machen. Wegen der Bequemlichkeit un-
terstützt Pascal auch den Textfile-Typ. Das Textfile ist ein spezieller Typ
vom "File of Char", aber nicht kompatibel damit! Textfiles bestehen aus ASC-
II-Zeichen, die in Zeilen von variabler Länge unterteilt sind. Jede Zeile
wird durch ein spezielles "Neue Zeile-Zeichen" beendet, der am Amiga das Li-
neFeed-Zeichen ist (ASCII-Code 10).
Da Textfiles (wie diese Anleitung und alle Sourcefiles) so gleich sind, ent-
hält Pascal viele verschiedene eingebaute Routinen zum Lesen und Schreiben
in und aus diesen. Die meisten der Standardtypen haben beispielsweise eine
spezielle Routine um diese in ASCII-Zeichen zu konvertieren und diese in ein
File zu schreiben. Diese Routinen werden automatisch vom Compiler aufgerufen,
um jeden Wert in einer Write-Prozedur in ein Textfile zu schreiben. Ähnliche
Routinen existieren zum Lesen derselben Typen. Die Beschreibungen der Lese-
und Schreibprozeduren weiter unten beschreiben genau, wie diese Conversion
funktioniert.
Es gibt auch eine spezielle Form der Read- und Write-Prozeduren, die speziell
für die Arbeit mit Textfiles gemacht wurden - die ReadLn- und WriteLn-Befeh-
le. Auch sie werden weiter unten beschrieben; beachtet aber, daß diese Rou-
tinen ausschließlich für Textfiles geeignet sind, da "typed Files" keine Zei-
len haben!
10.2 Öffnen von Files
---------------------
Es gibt verschiedene eng verwandte Routinen zum Öffnen von Pascal-Files. Zum
Öffnen von Input-Files (existierende Files die zum Lesen geöffnet werden)
kann man die ReOpen()-Funktion oder die Reset()-Prozedur verwenden.
Sie werden wie folgt definiert:
Reset(var FileVar : File or Text;
FileName : String;
BufferSize : Integer);
Die "Reset"-Prozedur öffnet ein File zum Lesen. Der FileVar-Parameter spezi-
fiziert die Pascal-Filevariable, die mit dem AmigaDOS-File assoziiert ist.
Der Filename muß auch den Pfad enthalten, sofern notwendig. Dieser kann jeder
vom AmigaDOS akzeptierte Filename sein, wodurch sich dieser auch auf den
Drucker, einem Console-Fenster oder ähnlichem beziehen kann.
Der Buffersize-Parameter (Puffergröße) spezifiziert die Buffergröße in Bytes.
Wenn PCQ versucht, das File zu öffnen, versucht es diesen Speicher als Puffer
zu reservieren. Falls das nicht möglich ist, setzt es IOResult und bricht ab.
Der BufferSize-Parameter ist komplett optional. Ist er nicht angegeben, wird
der voreingestellte Wert von 128 verwendet.
Wenn Ihr die automatische IO-Überprüfung benutzt, bricht das Programm mit ei-
nem Runtime-Error ab, wenn das File nicht geöffnet werden kann. Wenn Ihr das
Ganze manuell überprüft, wird lediglich IOResult gesetzt.
Die Variable FileVar sollte sich nicht auf ein File beziehen, das schon offen
ist. Entweder sollte das File zuerst geschlossen werden oder eine andere Fi-
levariable verwendet werden.
ReOpen(fname : String;
var fvar : file or text;
buffersize : Integer) : Boolean
ReOpen ist die Funktion-Form von Reset. Es versucht das File auf die selbe
Weise wie Reset zu öffnen, und falls alles ok ging, gibt es True zurück.
Gab es irgendein Problem, ist das Ergebnis False.
ReOpen setzt niemals IOResult, wenn Ihr also automatisches IO-Überprüfen be-
nutzt, bricht ReOpen nie das Programm ab. Wie in der Reset-Prozedur ist auch
hier der BufferSize-Parameter optional.
Wenn Ihr aber ein File zum Schreiben öffnen wollt (wobei jedes existierende
File desselben Namen gelöscht wird!), könnt Ihr die Rewrite-Prozedur oder die
Open()-Funktion benutzen.
Sie werden wie folgt definiert:
Rewrite(var FileVar : File or Text;
FileName : String;
BufferSize : Integer);
Rewrite öffnet ein AmigaDOS-File zum Schreiben und löscht dabei ein evtl.
existierendes File desselben Namens. Die Parameter sind identisch zu denen
des Reset-Befehls und die BufferSize ist immer noch optional.
ReWrite setzt IOResult. Das Verhalten von Rewrite ist hier identisch zu dem
von Reset.
Wie bei allen File-Öffnen-Routinen sollte man keine Filevariable verwenden,
die schon zu einem offenen File gehört.
function open(filename : string;
filevar : file of ..., oder Text;
BufferSize : Integer): Boolean;
Öffnet ein File für den Schreibzugriff. Sollte ein File gleichen Namens
bereits existieren, dann wird es durch diesen Befehl automatisch gelöscht.
Ist alles OK, wird True zurückgegeben, ansonsten False.
Denkt daran, daß das Öffnen eines existierenden Files zum Schreiben den In-
halt dieses Files löscht.
Open setzt IOResult nicht und der BufferSize-Parameter ist optional...
10.3 Schreiben in Files
-----------------------
Die Ausgabe in Pascal wird durch die Write- und Writeln-Prozeduren durchge-
führt. Anders als die meisten Pascal-Prozeduren, akzeptieren diese Routinen
eine unbegrenzte Anzahl an Parametern. Wenn Ihr in ein Textfile schreibt,
können die Parameter viele verschiedene Typen haben. Die Write-Prozedur hat
die folgende Basis-Form:
Write(var FileVar : Text or Typed File, Expressions...)
FileVar ist optional. Falls nicht angegeben, wird der Standard-Textfile-Out-
put an dessen Stelle gesetzt. Wenn die Filevariable ein "typed File" ist,
muß jeder Ausdruck vom Elementtyp sein. Sie werden in das File ohne Lücken
dazwischen geschrieben, formatiert wie sie auch im Speicher stehen.
Das Schreiben in ein Textfile ist eine komplett andere Geschichte. Zunächst
einmal kann jeder Ausdruck die Form "e:m:n" haben, wobei e der Ausdruck (Ex-
pression) ist sowie m und n Integerkonstanten. m spezifiziert die minimale
Feldgröße - falls das Item, das geschrieben werden soll kürzer als m Zeichen
groß ist, werden Leerstellen in das Feld geschrieben. n ist nur erlaubt, wenn
e real (reell) ist; es spezifiziert wie viele Zeichen rechts des Dezimal-
punkts geschrieben werden sollen. Die voreingestellten Werte für m und n sind
1 und 2. Was geschrieben wird hängt vom Typ ab, wie folgt:
Integer
Short
Byte Diese 3 Typen werden alle als ASCII-Ziffern-Sequenzen geschrieben.
Ihnen werden keine Leerstellen voran- oder nachgestellt, wobei bei
Short- und Integer-Werten ein Minus vorangestellt sein kann.
Real Der Ganzzahlteil (vor dem Dezimalpunkt) wird normal ausgegeben. Falls
n ungleich 0 ist, wird ein Dezimalpunkt gefolgt von n Nachkommastel-
len ausgegeben. Beachtet, daß bei reellen Zahlen m die Anzahl der
Ziffern links vom Dezimalpunkt angibt, nicht die volle Feldgröße (was
möglicherweise in der nächsten Version geändert wird).
Char Ein Zeichen wird genau als ein Zeichen ausgegeben, unverändert.
Array of Char
Das gesamte Feld wird Zeichen für Zeichen ausgegeben.
String Die gesamte Zeichenkette wird bis zum abschließenden NULL-Zeichen
ausgeben (wobei das NULL-Zeichen nicht ausgegeben wird).
Boolean Es wird entweder das Wort TRUE oder FALSE geschrieben, ohne anführen-
de oder abschließende Leerstellen.
WriteLn(var FileVar : Text; Expressions...)
Wie "Write", nur mit anschliessendem LineFeed. Kann nur bei Textfiles ver-
wendet werden.
10.4 Lesen aus Files
--------------------
Das Einlesen in Pascal wird von der Read-Prozedur durchgeführt. Wie bei den
Write-Prozeduren akzeptiert Read jede Anzahl von Parametern und viele Typen
der Parameter, wenn man von einem Textfile liest. Dabei gilt das folgende
Format:
Read(var FVar : Text or Typed File; Variable References....)
Der FVar-Parameter ist optional. Falls dieser fehlt, wird das Standard-Input-
Textfile verwendet.
Wenn sich FVar auf ein "typed File" bezieht, wird jede Referenz-Variable mit
den subsequenten File-Elementen gefüllt bis das EOF (Ende des Files) erreicht
wird oder irgendein Fehler auftritt. Das Einlesen eines Textfiles ist eine
etwas flexiblere Arbeit. Wie bei der Write-Anweisung, hängt auch hier das,
was passiert, vom gelesenen Typ ab, wie folgt:
Integer
Short
Byte Überspringt alle Leerzeichen und Tabs (alle Zeichen mit ASCII-Code
kleiner oder gleich 32). Ist das erste Zeichen mit einerm ASCII-Code
größer als 32 keine Ziffer oder ist das File-Ende erreicht, wird ein
IO-Error ausgegeben (siehe "Exit-Prozeduren").
Falls nicht, werden die Ziffern bis zur ersten Nicht-Ziffer eingele-
sen. Die sich ergebende Nummer wird zurückgegeben und er Filepointer
zeigt auf diese Nicht-Ziffer.
Real Ein Integer wird genau wie oben beschrieben eingelesen. Ist das näch-
ste Zeichen ein Punkt, werden die nächsten Ziffern wieder eingelesen
(als Nachkommastellen) bis zur 1. Nicht-Ziffer. Ein Real-Wert benö-
tigt weder einen Nachkommateil noch einen Dezimalpunkt.
Char Liest das nächste einzelne Zeichen.
Array of char
Liest solange Zeichen in das Datenfeld, bis das Datenfeld voll ist
oder ein EOL gefunden wird. Sollte vorher ein EOL gefunden werden,
dann wird ab dieser Stell der Rest des Datenfeldes mit Leerzeichen
aufgefüllt, wobei der Filepointer auf das EOL-Zeichen zeigt. Um das
loszuwerden, muß Readln aufgerufen werden.
String Liest Zeichen ein, bis ein EOL gefunden wird. Das EOL steht immer am
linken Ende des Datenflusses und wird dann automatisch durch ein
Null-Byte ersetzt. Der Filepointer bleibt beim EOL-Zeichen stehen.
Beachtet, daß diese Routine keine Längenüberprüfung durchführt. Ihr
müßt also sicherstellen, daß auch alle Zeichen des Strings eingelesen
werden können.
Boolean Nicht möglich.
Eine Reihe von Referenzvariablen in einer Read-Prozedur verhält sich genauso
wie jede andere der Referenzen in individuellen Read-Prozeduren. Somit ist
Read(Input, Var1, Var2);
identisch zu
Read(Input, Var1); Read(Input, Var2);
das genauso identisch ist zu
Read(Var1); Read(Var2);
ReadLn(var FVar : Text; Variable References...)
Readln ist identisch zu Read, mit der Ausnahme, daß nachdem alle spezifizier-
ten Variablen eingelesen wurden, dieser Befehl Zeichen bis zum nächsten EOL
frißt (incl. dem EOL). Dieser Befehl kann nur in Textfiles verwendet werden.
10.5 File-Puffer
----------------
Wie Standard-Pascal (aber im Gegensatz zu Turbo Pascal), erlaubt PCQ auf den
File-Puffer direkt zuzugreifen. Die Syntax der Referenz sieht wie folgt aus:
<File Variable Reference> ^
e.g. FileVar^
Dieser Ausdruck hat denselben Typ als die Elemente des Files selbst (Textfi-
les haben Char-Elemente). Bei Input-Files zeigt dieser Syntax nach dem näch-
sten Element, der durch die Read()-Prozedur eingelesen wird. Wenn EOF(File-
Var) TRUE ist, dann ist FileVar^ ungültig.
Bei Output-Files zeigt die File-Puffer-Referenz immer auf den Output-Puffer.
Das ist nicht der Wert, der mit der Write()-Prozedur ausgegeben wird - siehe
die Beschreibung von Put() weiter unten.
Logischerweise ist der Filepuffer, auf den Ihr mittels diesem Syntax zu-
greift, nur 1 Element lang, aber der eigentliche Puffer im Speicher kann be-
liebig groß sein. Siehe die File-Öffnen-Befehle für Informationen über das
Setzen der Puffergröße.
Wenn Ihr den Filepuffer benutzt, werdet Ihr wahrscheinlich einen oder beide
der folgenden Routinen benutzen:
Get(var FileVar : Text or Typed File)
Get bewegt lediglich den Filezeiger vom aktuellen Element zum nächsten. Das
kann DOS dazu veranlassen, den Buffer neu zu füllen, aber normalerweise ist
das bloß dazu gedacht, ein Feld in der Filevariable selbst anzupassen. Es ist
ein Fehler, diese Routine bei einem File aufzurufen, für das EOF(FileVar)
TRUE ist.
Jedes Lesen von einem "typed File" der Form "Read(FVar,Element)" kann wie
folgt umgesetzt werden:
Element := FVar^; { To assign the current value }
Get(FVar); { To advance the file pointer }
Put(var FileVar : Text or Typed File)
Put bewegt den Filezeiger hinter das aktuelle Element in einem Output-File
und schreibt den Puffer auf die Disk, wenn nötig. Jedes Statement der Form
"Write(FVar,Expr)", wobei FVar ein "typed File" ist, kann wie folgt umgesetzt
werden:
FVar^ := Expr; { Set the buffer value }
Put(FVar); { Commit the buffer }
Beachtet, daß solange bis die Put-Prozedur aufgerufen wird, alle Assignments
in den Filepuffer sich gegenseitig überschreiben.
10.6 Ein- und Ausgabe-Überprüfung (IO-Checken)
----------------------------------------------
Die Ein- & Ausgabe kann wohl mehr unvorhergesehene Fehler verursachen als je-
der andere Programmierbereich. PCQ-Pascal fängt IO-Fehler in einer von 2 We-
gen ab. Der erste nennt sich automat. IO-Checken - PCQ überprüft dabei nach
jedem IO-Vorgang auf Fehler. Ist dabei ein Fehler aufgetreten, bricht das
Programm mit einem Runtime-Error ab. Das ist die voreingestellte Methode!
Wenn Ihr das automatische Überprüfen mittels der {$I-} Compilerdirektive ab-
schaltet, müßt Ihr selbst auf Fehler überprüfen. Das geschieht, indem man die
IOResult-Funktion aufruft, die 0 zurückgibt, sofern alles gut gegangen ist.
Jede andere Zahl bedeutet, daß ein Fehler aufgetreten ist. Wenn Ihr IOResult
aufruft, löscht Ihr automatisch dessen Wert. Wenn Ihr also den Fehlercode ir-
gendwo später noch benutzen wollt, müßt Ihr diesen in einer Variable spei-
chern.
IOResult wird durch jede IO-Routine gesetzt. Alle IO-Routinen funktionieren
nicht, wenn IOResult nicht gleich 0 ist. Ihr solltet also so oft wie möglich
nach Fehlern prüfen, um das Überspringen von READs und WRITEs zu vermeiden.
Die Werte, die von IOResult zurückgegeben werden, sind dieselben wie die Run-
time-Fehler - mögliche Werte sind in der Sektion "Exit-Prozeduren" beschrie-
ben.
10.7 Standard Ein & Ausgabe
---------------------------
Eine der trickreichen Teile der Amiga-Programmierung ist, daß ein Programm
von 2 verschiedenen Umgebungen gelauncht werden kann. Das CLI ruft ein Pro-
gramm ähnlich wie MS-DOS auf - das Programm hat klare Input- und Output-Kanä-
le. Die Workbench auf der anderen Seite bietet nichts dergleichen. Ein Pro-
gramm, das Input- und Output-Kanäle benötigt, muß diese selbst anlegen.
Alle PCQ-Programme richten eine Art von Standard-Input- und -Output-Kanäle
ein, auf die durch die Textfiles Input und Output zugegriffen werden kann.
Was diese Files repräsentieren hängt natürlich von der Umgebung ab, von der
das Programm aufgerufen wurde.
Nehmen wir erstmal einen einfachen Fall. Wenn Ihr ein Programm vom CLI star-
tet, wirken Input und Output auf das CLI-Fenster. Wenn Ihr Ausgabe-Umleitung
verwendet, wirken Input und Output auf die Kanäle, die in der Kommandozeile
angegeben sind (siehe auch das AmigaDOS-Handbuch).
Wenn das Programm von der Workbench gestartet wurde, durchläuft der Startup-
Code verschiedene Schritte, um Input und Output einzurichten. Zuallererst
wird der Wert der Standard-String-Typed-Konstante StdInName untersucht. Ist
dieser Nil, wird kein File geöffnet und im Bezug auf Input entstehen große
Probleme. Wenn nicht, versucht der Startup-Code das File zu öffnen. Ist das
nicht möglich, bricht es mit dem Run-Time Error 53 ab. Wenn alles gut gegan-
gen ist, ist der Input eingerichtet.
Als nächstes untersucht der Startup-Code den StdOutName. Ist dieser Nil, wird
der Output nicht eingerichtet, wodurch alle Write-Prozeduren ein spezielles
File benutzen sollten. Zeigt StdOutName auf denselben String wie StdInName
(nicht 2 Strings mit denselben Inhalten - es muß auf dieselbe Speicherstelle
zeigen), dann wird Output mit demselben Filehandle wie Input verbunden. Die
voreingestellten Werte für StdInName und StdOutName haben diese Möglichkeit.
Wenn einer davon fehlschlägt, versucht der Startup-Code, ein File gemäß Std-
OutName zu öffnen; funktioniert das, wird der Output abhängig davon zugewie-
sen. Ansonsten bricht das Programm mit dem Runtime-Error 57 ab.
Die voreingestellten Werte für die beiden Kanäle sind in der PCQ.lib wie
folgt definiert:
CONST
StdInName : String = "CON:0/0/640/200/";
StdOutName : String = StdInName;
Abhängig zu den oben spezifizierten Regeln, resultiert das normalerweise in
einem Full-Screen-Console-Fenster, das geöffnet wird, mit Input daraus und
Output dahin.
Beachtet, daß StdInName und StdOutName als Typen-Konstanten definiert sind.
Sie können keine Variablen sein, weil sie vor dem gesamten Programmcode aus-
geführt werden. Ist das ein größeres Problem, könnt Ihr beide als Nil defi-
nieren und innerhalb Eures Programmes Input und Output als irgendwas belie-
biges öffnen. Ihr solltet besser Input und Output nur dann nochmals benutzen,
wenn Euer Programm von der Workbench gestartet wurde (Ihr könnt das überprü-
fen, indem Ihr GetStartupMsg aus "Utils/Parameters.i" aufruft).
Wenn Ihr Euer Programm mit dem "-s" klein-initializierten Code-Schalter kom-
piliert habt, ist all das aus: Input und Output werden nicht eingerichtet,
und auch keine der anderen IO-Routinen wird initialisiert. Siehe auch "Small
Initialization Code".
11. Zeichenkettenvariablen (Strings)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PCQ-Pascal hat Strings ähnlich zu denen von C, und absolut nicht ähnlich zu
Turbo Pascal. Das ist natürlich nicht so gut, da man TurboPascal-Strings viel
einfacher arbeiten kann.
Strings können als '^char'- Typen angesehen werden. Somit belegen alle String-
Variablen genau 4 Bytes. Diese enthalten die Adresse des eigentlichen Textes.
Der Text kann beliebig lang sein. Er wird mit dem Zeichen Chr(0) beendet.
Ein Statement wie:
StringVar := "A String"
beinhaltet die Adresse des konstanten Strings in der Stringvariable, eher als
dies in den String selbst in irgendeine existierende Lücke zu kopieren.
Im Programmtext wird jeder String mit doppelten Anführungszeichen einge-
grenzt, anstelle der einfachen, wie sie in "char"-Datenfeldern zu finden
sind. Also:
"Ein String" ist wirklich ein String, während
'Kein String' ein array [1..11] of char ist
Strings sind als einfache Zeiger definiert, wodurch "StringVar^" korrekt ist.
Dieser ist vom Typ Char. Er zeigt auf das erste Byte im Stringpuffer. Aber
im Gegensatz zu normalen Zeigern können die einzelnen Zeichen eines Strings
ebenfalls durch eine Subscript-Notation erreicht werden. Um beispielsweise
das 4. Zeichen eines Strings zu bekommen, könnte man "StringVar[3]" benutzen
(StringVar[0] ist das 1. Element!).
11.1 Reservieren von String-Speicher
------------------------------------
Da die eigentlichen String-Variablen nur eine Adresse beinhalten, muß ein be-
stimmter Speicher für den eigentlichen Text reserviert werden. In PCQ-Pascal
muß man den Speicher ausdrücklich vom System reservieren oder die Adresse ei-
ner Puffer-Variablen auf den String assignen. Dazu gibt´s verschiedene Funk-
tionen, die alle im Include-File "StringLib.i" definiert sind, um Speicher
für Strings zu reservieren. Diese sind:
AllocString(Size : Integer) : String
AllocString gibt einfach einen uninitialisierten Speicherbereich der Mindest-
größe "Size" Bytes an. Dieser Speicher wird als PCQ-Speicher behandelt, wo-
durch es nach der Benutzung ans System zurückgegeben wird (siehe "Speicher-
verwaltung").
strdup(InString : String) : String
Die strdup-Funktion (String-Verdoppelung) entspricht dem folgenden:
temp := AllocString(strlen(InString));
strcpy(temp,InString);
strdup := temp;
Es wird somit genügend Speicher reserviert, um den Input-String darin zu hal-
ten. Danach wird eine Kopie des Input-Strings in den neuen Puffer gemacht.
Dieser Speicher wird auch als PCQ-Speicher betrachtet (mit allen daraus re-
sultierenden Konsequenzen).
Die andere Möglichkeit, einen String-Puffer einzurichten, ist, die Sting-Va-
riable auf die Adresse eines irgendwo deklarierten Arrays zu assignen.
Var
String1 : String;
Buffer1 : Array [0..127] of Char;
begin
String1 := Adr(Buffer1);
Dadurch wird ein 128 Zeichen großer Puffer für String1 eingerichtet. Versucht
Ihr aber, einen 129 Zeichen großen String in diesem Puffer zu speichern, tre-
ten alle Arten von Problemen auf - seid also vorsichtig!
11.2 Benutzung von Escape-Zeichen
---------------------------------
Eine weitere interessante Tatsache bei der Stringbehandlung ist die Möglich-
keit, ESC(APE)-Sequenzen einzubauen. Das ist vor allem dazu nützlich, Zei-
chen, die nicht so einfach in ein Programm-Sourcefile geschrieben werden kön-
nen, doch benutzen zu können. Sobald Sie einen Backslash (\) in irgendeinen
Text (Char, Array of Char oder String) schreiben, wird das unmittelbar fol-
gende Zeichen besonders bearbeitet.
Bis jetzt werden folgende Escape-Sequenzen von PCQ unterstützt:
(chr heißt ASCII-Zeichen-Nummer - Anm.d.Ü.)
\n Line Feed, chr(10)
\t Tab, chr(9)
\0 Null, chr(0)
\b Backspace, chr(8)
\e ESC, chr(27)
\c CSI (Control Sequence Introducer), chr($9B)
\a Attention, chr(7)
\f Form Feed, chr(12)
\r Carriage Return, chr(13)
\v Vertical Tab, chr(11)
Jedes andere Zeichenläuft ohne Umänderung durch den Compiler, sodaß auch fol-
gendes möglich ist:
\\ ergibt einen einzelnen Backslash (\)
\' ergibt ein einzelnes Anführungszeichen
\" ergibt ein doppeltes Anführungszeichen
Somit wird ein String wie
"A\tboy\nand\\his \"dog.\""
so ausgegeben:
|A boy
|and\his "dog"
(wobei | den linken Rand repräsentiert)
11.3 StringLib
--------------
StringLib.i ist eine Include-Datei im Utils-Verzeichnis, die ein paar Funk-
tionen in der Art von C für die Benutzung mit Strings beinhalten. Diese Funk-
tionen werden im Include-File selbst erklärt. Der Sourcecode für diese Rou-
tinen befindet sich im Sourcecode der runtime-library, die man auf Anfrage
beim Autor erhalten kann.
12. Speicher-Verwaltung
^^^^^^^^^^^^^^^^^^^^^^^
Eine der Probleme bei der Amiga-Programmierung ist, daß es kein Resource-
Tracking gibt. In einem MS-DOS-Programm kann man wie verrückt Files öffnen
und Speicher reservieren und dann das Programm bloß schließen und alles ist
ok. Auf dem Amiga würden alle Files geöffnet und der Speicher reserviert
bleiben. Man muß also "selbst aufräumen".
Um Euch dabei zu helfen, benutzt PCQ-Pascal ein spezielles Speicherreservie-
rungsschema, das die Intuition-AllocRemember-Routine benutzt, um die Spur
jeder Speicherreservierung zu behalten. Bricht das Programm ab, wird jeder
Speicher, der nicht freigegeben wurde, automatisch von einer Exit-Prozedur
freigegeben.
Das funktioniert aber nur bei Speicher, die durch einer der speziellen PCQ-
Routinen New, AllocString (von Utils/StringLib.i) oder GetMem (von Utils/PCQ-
Memory.i) reserviert wurde. Dieser Speicher wird dann PCQ-Speicher genannt,
er unterscheidet sich aber in keinerlei Hinsicht vom normalen Systemspeicher,
abgesehen davon, daß er zurückgegeben wird.
Obwohl er automatisch freigegeben wird, sollte man den Speicher sobald wie
möglich wieder freigeben - er könnte von anderen Programmen gebraucht werden.
Um PCQ-Speicher freizugeben benötigt man Dispose, FreeString oder FreePCQMem.
Macht Ihr das nicht, wird der Speicher zwar ans System zurückgegeben, aber
die PCQ-Speicherroutinen merken davon nichts. Sie versuchen daher, diese er-
neut zu reservieren, wenn das Programm abbricht -> Guru.
PCQ-Speicher wird immer mit den Flags MEMF_PUBLIC und MEMF_CLEAR reserviert.
Wenn Ihr Chip-Memory benötigt, solltet Ihr die normale Exec-Funktion AllocMem
(aus Exec/Memory.i) benutzen.
12.1 HeapError (Überlauf-Fehler)
--------------------------------
Was passiert nun, wenn die Speicherroutinen vom PCQ keinen Speicher reservie-
ren können, die ein User verlangt? - Das hängt vom Wert von HeapError ab, ei-
ner Standard-Address-Variable. HeapError beinhaltet die Adresse einer Funk-
tion, die einen Header der folgenden Form hat:
Function HeapFunc(Size : Integer) : Integer;
Der Size-Parameter ist die Speichermenge, die der Reservierer zu bekommen
versucht hat, und der zurückgegebene Integerwert bestimmt, wie der Reservie-
rer auf das Problem antwortet. Wird 0 zurückgegeben, bricht der Reservierer
mit dem Runtime-Error 54 ab. Bei 1, gibt der Reservierer Nil zurück, wodurch
man nach jedem New, AllocString oder GetMem auf einen Nil-Wert prüfen muß.
Gibt HeapError 2 zurück, versucht der Reservierer dieselbe Reservierung noch-
mals. Das gibt Euch die Möglichkeit, etwas Speicher freizugeben, wenn mög-
lich. Schlägt das nochmals fehl, wird HeapError nochmals aufgerufen.
Um eine Funktion wie HeapError zu installieren, muß man etwas verwenden wie:
HeapError := @HeapFunc;
... wobei HeapFunc wie oben definiert ist.
13. Exit-Prozeduren
^^^^^^^^^^^^^^^^^^^
Exit-Prozeduren sind Routinen, die nachdem das Hauptprogramm fehlgeschlagen
ist, durchlaufen werden sollen (oder falls ein Runtime-Error aufgetreten
ist). Sie werden dazu benutzt, um Resourcen ans System zurückzugeben, oder
um nett abzubrechen, oder sogar um Runtime-Errors rückgängig zu machen.
Das funktioniert so: Bricht ein Programm ab - warum auch immer - untersucht
PCQ den Wert von ExitProc, einer Standard-Address-Variable. Ist sie nicht
Nil, setzt PCQ ExitProc auf Nil und ruft die Prozedur auf, auf die er vorher
gezeigt hat. Ist die Prozedur abgelaufen, überprüft PCQ nochmals ExitProc und
ruft weiter Exit-Prozeduren auf bis ExitProc Nil ist. Der normale PCQ-Initia-
lisierungs-Code richtet eine Exit-Prozedur ein, die den gesamten Speicher,
der durch New oder AllocString reserviert wurde, wieder freigibt, und außer-
dem alle offenen Pascal-Files schließt. Normale Programme haben somit minde-
stens eine Exit-Prozedur.
Um aus einer Prozedur eine Exit-Prozedur zu machen, muß man zuerst eine Rou-
tine ohne Parameter definieren. Danach muß die ExitProc-Standard-Variable auf
die Adresse der Routine gesetzt werden. In den meisten Fällen werdet Ihr wohl
den letzten Wert von ExitProc speichern wollen, wodurch alle anderen Exit-
Prozeduren auch laufen können. Innerhalb der Exit-Prozedur selbst, solltet
Ihr ExitProc auf den vorherigen Wert zurücksetzen.
Es gibt 2 zusätzliche Variablen, die mit Exit-Prozeduren zusammenhängen und
die nur innerhalb der Prozedur wahr ist, wenn sie als Exit-Prozedur aufgeru-
fen wurde. Sie existieren immer, aber Ihr Wert wird nur gesetzt, wenn das
Programm abbricht. Diese sind:
ExitCode ExitCode beinhaltet den Wert jedes Runtime-Errors, der das Ab-
brechen des Programms verursacht hat. Dies ist entweder ein Wert,
den Ihr durch die Exit()-Routine übergeben habt, oder ein Run-
time-Errorcode.
ExitAddr ExitAddr ist die Addresse, bei der ein Runtime-Error aufgetreten
ist. Dies ist nur wahr, wenn ExitCode nicht null ist. Er kann
(theoretisch) dazu benutzt werden, um von einer Routine zurück-
zukehren, aber Ihr müßt Euch dann schon ziemlich gut mit dem ei-
gentlichen Runtime-Code auskennen, um das zum Laufen zu bekommen.
14. Laufzeitfehler (Runtime-Errors)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Einige Dinge verursachen Lauzeitfehler. Passiert das, wird ExitCode auf einen
speziellen Wert gesetzt, der schließlich an AmigaDOS zurückgegeben. AmigaDOS
hingegen ignoriert das normalerweise, wodurch Ihr das normalerweise niemals
zu Gesicht bekommen werdet. Die beiden einzigen Wege, um den Rückgabewert zu
erhalten, sind das Programm in einem Script laufenzulassen, oder es mit dem
Run-Befehl von AmigaDOS laufen zu lassen. In beiden Fällen gibt AmigaDOS
"ProgramName failed returncode #" aus, sofern der Wert ungleich 0 ist.
Folgende Runtime-Errors werden von PCQ-Programmen erzeugt:
Code Beschreibung
----------------------------------------------
50 kein Speicher für IO-Puffer (1)
51 Read nach EOF (1)
52 Input-File ist nicht geöffnet (2)
53 StdInName konnte nicht geöffnet werden (3)
54 New() ist fehlgeschlagen (4)
55 Integer durch 0 geteilt
56 Output-File nicht geöffnet (2)
57 StdOutName konnte nicht geöffnet werden (3)
58 EOF vor dem 1. Zeichen gefunden beim Einlesen eines Integers (1)
59 keine Zeichen gefunden beim Einlesen eines Integer (1)
60 Reichweiten-Fehler
Die folgenden AmigaDOS-Fehlercodes können durch einen Aufruf von Open,
ReOpen, Write, Read, usw. entstehen: (1)
103 freier Speicherplatz reicht nicht as
202 Objekt wird gerade benutzt
203 Objekt existiert bereits
204 Verzeichnis nicht gefunden
205 Objekt nicht gefunden
206 Fehlerhafte Fenster-Spezifikation
210 Fehlerhafter Komponenten-Name
212 Objekt vom falschen Typ
213 Diskette nicht validiert
214 Diskette schreibgeschützt
218 Device nicht gemountet
221 Diskette voll
223 File schreibgeschützt
224 File lesegeschützt
226 keine Disk im Laufwerk
(1) Diese Fehler verursachen nur Laufzeitfehler, wenn automatisches IO-Error-
Überprüfen eingeschaltet ist (voreingestellt). Habt Ihr die {$I-} Direk-
tive verwendet, um das IOResult selbst zu überprüfen, bricht das Programm
nicht automatisch ab.
(2) PCQ kann nicht immer abbrechen, wenn das File nicht offen ist. Wenn es
kann, wird dieser Fehler verursacht. Wenn nicht, gibt´s einen Systemab-
sturz!
(3) Diese Fehler treten im Initialisierungscode auf bevor irgendwelche Exit-
Prozeduren aufgerufen werden.
(4) Siehe Sektion "Speicherverwaltung".
(5) Tritt nur bei Range-Checking-ON auf (mittels der {$R+} Direktive ein-
schaltbar)
15. Compiler-Direktiven
^^^^^^^^^^^^^^^^^^^^^^^
Es gibt diverse Optionen, die PCQ unterstützt, die aber nicht für alle Pro-
gramme nützlich sind. Zum Beispiel sollte eine große Applikation keinen Run-
time-Error verursachen, wenn es ein File nicht öffen kann. Daher erlaubt PCQ-
Pascal verschiedene Aspekte des von Eurem Code generierten Programm vorzube-
stimmen.
Diese Optionen werden durch Compiler-Direktiven kontrolliert. Diese sind in
Kommentaren enthalten, und müssen mit dem Dollarzeichen "$" als 1. Zeichen
im Kommentar beginnen. Direkt danach muß ein Zeichen folgen, daß die Direk-
tive näher bestimmt, die dann wieder von keinem oder mehreren Zeichen für
zusätzliche Informationen gefolgt wird.
Ein Komma kann zum Trennen verschiedener Direktiven benutzt werden, das sieht
dann so aus (schaltet IO-Cheken aus und Range-Checken an):
{$O-,R+}
Die folgenden Direktiven werden unterstützt:
{$A Any number of assembly instructions}
Dieses Kommando bindet Assembler-Instruktionen mit in das assemblierte File
ein.
{$B+} or {$B-}
Die $B Direktive schaltet Kurzschluß-Berechnungen (short-circuit evaluations)
an/aus. Benutzt Ihr $B+, um die Kurzschluß-Berechnungen anzuschalten, was
voreingestellt ist, dann berechnet PCQ boolean Ausdrücke normal. Sobald aber
das Endergebnis des Ausdrucks bekannst ist, wird der Rest des Ausdrucks über-
sprungen. D.h. z.B. in einer Reihe von AND-Fällen wird, sobald ein Fall FALSE
ergibt, der Rest des Arguments nicht berechnet. In einer Reihe von OR-Fällen
wird, sobald ein Fall TRUE ergibt, der Rest des Arguments ebenfalls nicht be-
rechnet. Der Ausdruck wird bei Kurzschluß-Berechnung wird der Ausdruck immer
von links nach rechts berechnet.
Die Kurzschluß-Berechnung macht die Boolean-Berechnung etwas schneller, vor
allem in langen Gleichungen.
Schaltet Ihr die Kurschluß-Berechnungen mittels der $B- Direktive aus, wird
der gesamte boolean Ausdruck berechnet, auch wenn das, was herauskommt, zwei-
fellos feststeht. Der Ausdruck wird nicht unbedingt von links nach rechts be-
rechnet.
{$I "fname"}
Dadurch wird das File "fname" als Eingaberoutine an der aktuellen Fileposi-
tion mit eingebunden. Nach der Einbindung wird mit dem Originalfile fortge-
fahren. Nach dieser Direktiven können keine anderen Direktiven mehr (in der
selben Klammer) stehen.
Um das mehrmalige Einlesen desselben Files zu verhindern, merkt sich der Com-
piler eine Liste aller Files, die bereits eingelesen wurden, und liest bei
nochmaliger Einlese-Aufforderung das File nicht mehr ein. Beachtet, daß nur
der eigentliche Filename, nicht aber der Verzeichnispfad, verglichen wird.
Somit sollten alle Include-Files verschiedene Namen haben!
Fast alle Beispielprogramme demonstieren die Benutzung der Includefiles.
{$I+} or {$I-}
Die andere Art der $I Direktive bestimmt das Verhalten Eurer Programme nach
Ein/Ausgabefehlern (IO-Errors). Das voreingestellte Verhalten, das mit dem
der {$I+} Direktiven übereinstimmt, ist, einen Laufzeitfehler zu verursachen,
sobald ein Fehler in einer IO-Routine erkannt wird. Mit der anderen Option,
die mit der {$I-} Direktiven einschaltbar ist, erwartet das Programm die
Überprüfung der IOResult-Funktion nach IO-Operationen und die davon abhängige
Behandlung der Fehler. Siehe "Ein und Ausgabe".
{$SN} oder {$SX} oder {$SP} oder {$SD}
Die $S Option kontroliert das Speichern (mit Speichern ist hier das Zwischen-
speichern im Arbeitsspeicher gemeint - Anm.d.Ü.) von globalen Variablen und
Typenkonstanten.
Die N-Option (Normale Speicherung) sagt dem Compiler, daß er für alle folgen-
den globalen Variablen Speicher im Datensegment reservieren soll, und auch
eine externe Definition für den Identifier ausgeben soll (XDEF in Assembler),
der von externen Routinen benutzt werden kann. Das ist das normale Speicher-
schema für PCQ-Programme.
Die X-Option (eXterne Speicherung) sagt dem Compiler, daß alle folgenden Va-
riablen außerhalb des Programms definiert werden. Daher sollte der Compiler
keinen Speicher für die Variablen reservieren, sondern nur eine externe Re-
ference erzeugen soll (XREF in Assembler). Das ist das normale Speicherschema
für externe Files.
Die P-Option (Private Speicherung) sagt dem Compiler, daß er Speicher für
globale Variablen reservieren soll, aber den Identifier nicht selbst expor-
tieren. Das dient dazu, externen Files globale Variablen zu erlauben, die
keine Auswirkung auf das Hauptprogramm haben.
Die D-Option (vorDefinierte Speicherung) setzt die Speicherung auf den Stan-
dard-Wert zurück. In einem externen File wirkt das wie {$SX} und in einem
normalen Programm wie {$SN}.
{$O+} or {$O-}
Diese Optionen entsprechen den {$I+} und {$I-} Optionen.
{$R+} or {$R-}
Die $R Direktive bestimmt, ob der Compiler überprüfen soll, ob Indexwerte in-
nerhalb der Arrayweite liegt. Das voreingestellte Verhalten ist {$R-}, d.h.
daß der Compiler den Extra-Code nicht generiert. Spezifiziert man {$R+}, er-
zeugt der Compiler dann (wenn ein Indexwert außerhalb eines Arrays liegt -
Anm.d.Ü.) einen Runtime-Error 60. Dies erweitert und verlangsamt den Code
natürlich beträchtlich, weshalb dies nur in der Testphase eines Programmes
durchgeführt werden sollte.
16. Typen-Konvertierungen
^^^^^^^^^^^^^^^^^^^^^^^^^
In einer stark typisierten Sprache wie Pascal muß es einen Weg geben, um die
Typenregeln zu umgehen. Dabei benutzt PCQ dieselben Methode wie TurboPascal
und Modula-2. Der Format für eine Type-Cast (Konvertierung) ist wiefolgt:
<Type ID> ( <Expression or Variable Reference> )
Das sieht nur wie eine Funktion aus, ist aber keine. Ein Type-Cast generiert
keinen Code - sie läßt nur Code durch den Compiler kommen. Das kann aller-
dings zu ernsthaften Problemen führen. Ein Type-Cast wie "Short(Enumerate-
Var)" verursacht Probleme, weil aufgezählte Variablen normalerweise 1 Byte
lang sind, während Ihr dem Compiler mitgeteilt habt, es als 2 Byte-Wert an-
zunehmen, wobei das andere Byte dann undefiniert ist. Der korrekte Weg um
das zu schaffen wäre die Benutzung der Ord()-Funktion gewesen, oder es zumin-
dest stattdessen in 1 Byte zu konvertieren.
Der Unterschied zwischen Short() und Ord() in diesem Beispiel ist, daß Ord()
eine Typentransferfunktion ist, während Short() absolut keine Funktion ist.
Hier wäre da noch ein 2. Beispiel:
Writeln(Integer(2.0));
Writeln(Trunc(2.0));
Diese beiden Zeilen schreiben nicht dieselben Werte. Trunc(), eine andere
Typentransferfunktion, konvertiert den reellen Wert 2.0 in den Integer-Wert
2, bevor dieser geschrieben wird. Die Integer-"Funktion" macht garnichts,
wodurch das Programm lediglich den reellen Wert ausgibt, als ob dieser ein
Integer-Wert wäre, was was sicherlich eine absurde Zahl produziert.
Generell sollte ein Ausdruck nicht in einen größeren Ausdruck konvertiert
werden, und das Konvertieren eines einfachen Ausdrucks (eine Ordnungs- oder
reelle Zahl bzw. ein Zeiger-Wert) in einen komplexen Typ (ein Array oder eine
Record-Referenz) oder ungekehrt wird fast immer Nonsens-Ergebnisse liefern.
17. Kurzer Anfangscode (Small Initialisation Code)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Wenn Ihr ein Programm erzeugen wollt, das nicht den Kopf eines normalen PCQ-
Programms hat, ist diese Option recht nützlich. Wenn Ihr beispielsweise keine
Pascalfiles benutzt, benötigt Ihr den gesamten Code von Writeln() nicht. Wenn
Ihr also den Startup- und Schlußcode, den PCQ minimal an Euer Programm an-
fügt, kürzen wollt, könnt Ihr die "-s" Option (für small = kurz) in der Kom-
mandozeile angeben. Wenn Ihr z.B. Tiny.p mit dem kurzen Anfangscode kompilie-
ren wolltet, müßtet Ihr folgende Zeile eingeben:
Pascal Tiny.p Tiny.asm -s
Der Programmkopf, den Ihr dabei meidet, ist jedoch nicht unnötig groß, und
eines der Probleme, die Ihr ohne diesen habt, ist, daß Ihr ihn nicht benutzen
könnt. Außerdem könnt Ihr, wenn Ihr die "-s" Option in der Kommandozeile be-
nutzt, keine Pascal-IO-Routinen benutzen. Diese sind:
Write, WriteLn, Read, ReadLn, Get,
Put, Open, ReOpen, Close, IOResult
Der Compiler übergeht Referenzen an alle diese Identifier, ausgenommen von
IOResult. Beachtet aber, daß der Kompiler niemals Referenzen in separat kom-
pilierten Files (externen Files) oder in Objektcode-Libraries sehen wird.
Die Moral der Geschichte ist, daß Ihr extrem vorsichtig mit dieser Option
sein solltet, wenn Ihr keinen Systemabsturz erzeugen wollt...
Was fehlt also genau? - Der normale Startup-Code macht das folgende:
1. Datenaustausch mit der Kommandozeile oder Workbench-Mitteilung
2. öffnet die Intuition.-, Dos.- und MathFFP.library
3. richtet die Ein- und Ausgabe ein und öffnet ein Fenster, wenn nötig
4. initialisiert die Speicherreservierungsliste und die OpenFile-Liste,
um diese beim Verlassen des Programms freigeben zu können
5. richtet eine Exitprozedur ein, die Speicher und Files wieder freigibt
Wie Ihr seht, sind darunter ein paar zieml. große Routinen: Open(), Close(),
die Exit-Prozedur usw. Die Kurze-Anfangscode-Routine macht daher folgendes:
1. Datenaustausch mit der Kommandozeile oder Workbench-Mitteilung
2. öffnet die Intuition.-, Dos.- und MathFFP.library
3. initialisiert die Speicherliste und richtet eine einfache Routine zur
Freigabe alles New()-Speichers ein
Und das wär´s dann auch schon. Nach den Ergebnissen des Autors belegt das
Kompilat vom Programm "Program Test; begin end." normalerweise rund 2,5 KB,
während es weniger als 700 Bytes belegt, wenn der Kurze Anfangscode verwendet
wird.
18. Externe Files
^^^^^^^^^^^^^^^^^
Bei der Entwicklung des Compilers stellte sich heraus, daß das Schreiben ei-
nes ganzen Programms in nur 1 Sourcefile unhandlich ist, so waren z.B. die
Kompilierzeiten wahnsinnig lang. Um die Sachen etwas aufzuteilen, wurde eine
Methode zur getrennten Kompilierung in der Art von TurboPascals Units ent-
wickelt (die allerdings nicht ganz so mächtig sind).
Es können Funktionen ähnlich wie in anderen Pascal-Compilern als
Extern deklariert werden:
Externe Files haben das folgende Format:
<External File> ::= External; <Definitions>
In anderen Worten, beginnt ein externes File mit dem reservierten Wort "Ex-
ternal", gefolgt von einem Semikolon und einer beliebigen Anzahl von Defini-
tionsblocks, welche, wie oben definiert, Funktionen, Prozeduren, Variablen,
usw. sein können.
Immer wenn eine Prozedure oder Funktion definiert wird, exportiert PCQ-Pascal
die Identifier, wodurch sie von anderen Files benutzt werden können. Diese
anderen Files beziehen sich auf die Routine, indem eine externe Referenz in
Ihrem Code eingebunden wird (siehe "Externe Referenzen" in der "Prozeduren
und Funktionen" Sektion). Wenn Ihr also die Prozedur DoIt in Eurem externen
File definiert, würdet Ihr die folgende Deklaration in jedem anderen File an-
geben, in dem die Routine benutzt wird:
Procedure DoIt;
External;
Diese Deklaration teilt dem Compiler mit, daß er den Identifier von einem
anderen File importieren soll.
Die Definintion von globalen Variablen und Typen-Konstanten in einem externen
File ist etwas komplizierter. Um auf eine globale Variable Eures Hauptpro-
gramms in Eurem externen File zugreifen zu können, wird die Deklaration einer
globalen Variable in einem externen File als eine Variable, die aus einem an-
deren File importiert werden soll, angesehen. Globale Variablen können also
voreingestellterweise nur in normalen Programmfiles definiert werden. Die
Deklaration einer globalen Variable in einem externen File führt also zu kei-
ner Speicherreservierung! - Diese Voreinstellung kann jedoch mittels der $S
Direktiven verändert werden.
Wenn Ihr ein externes File benutzt habt, müßt Ihr sichergehen, daß seine Rou-
tinen im schließlich ausführbaren Programm eingebaut werden. Um dies zu tun,
muß der Filename im Linkerbefehl wie folgt eingebaut werden:
Blink Haupt.o Extern1.o ... to HauptProgramm Library PCQ.lib
In anderen Worten heißt das, daß der Einbau jedes externen Objektfiles (die
Ausgabe des Assemblers) im Linkerbefehl nach dem Mainfile aber vor dem
Schlüsselwort erfolgen muß. Die Reihenfolge ist dabei egal.
Während Pascal eine case-insensitive (keine klein-groß-Unterscheidung) Spra-
che ist, ist Assembler nicht. Somit muß, wenn Ihr externe Referenzen benutzt,
die erste Referenz in Eurem Programm (entweder die Variablendeklaration oder
der eigentliche Kopf der Prozedur oder Funktion) dieselbe Schreibweise wie
die Definition im externen File aufweisen. Weitere Benutzungen können eine
beliebige Schreibweise haben - lediglich die erste wird dazu benutzt, um die
Referenz zu erzeugen.
19. Einige Anmerkungen für Assembler-Programmierer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In früheren Versionen des Compilers waren die Register von d2 bis d7 sowie
a2 und a3 immer erreichbar. Außerdem waren die Scratch-Register auch zwischen
Statements erreichbar. Das stimmt nicht mehr, wodurch wohl einige Assembler-
routinen umgeschrieben werden müssen.
PCQ V1.2 benutzt die Register viel effizienter, d.h. daß alle Datenregister
benutzt werden können. PCQ reserviert Register von d7 abwärts, um Unteraus-
drucksergebnisse zu halten, wodurch die Wählbarkeit von Registern innerhalb
von Ausrücken vom Pascal-Code abhängt. Zwischen Statements sind alle Daten-
register einmal wieder wählbar.
Adreßregister werden wie vorher benutzt: a7 ist der Stack-Zeiger, a6 zeigt
auf die Library-Base, a5 der Stack-Frame-Zeiger und a4 der Zeiger auf vor-
herige Frames. Der Compiler kann a3 und a2 während Ausdrücken verwendet wer-
den, um zwischenzeitliche Adreßwerte zu halten.
Die Systemroutinen verwenden d0,d1,a0 und a1 als Scratch, bewahren aber alle
anderen Register.
20. Fehlermeldungen
^^^^^^^^^^^^^^^^^^^
Wenn der Compiler einen Fehler erkennt, gibt er die aktuelle und letzten Zei-
len vor dem Fehler aus, und hebt den Teil hervor, in deren Gegend der Fehler
aufgetreten ist. In der nächsten Zeile wird die Zeilennummer und die aktuelle
Prozedur oder Funktion sowie etwas (hoffentlich) beschreibender Text angege-
ben.
Der erste Error, den PCQ ausgibt, ist normalerweise ziemlich genau. Danach
werden aber auch Fehlermeldungen ausgeben, die überhaupt nicht existieren.
Dies sollte man am besten ignorieren. Der Compiler bricht daher nach 4 Feh-
lermeldungen ab.
Mit der "-q" quiet (ruhig) Befehlszeilenoption bekommen die Fehlermeldungen
eine regulärere Form, und zwar:
"source file name" At ##,## : Error Text
Dabei gibt das 1. ## die Zeile und das 2. ## die Spalte an. Das macht das
Programmieren automatischer Compilier-Routinen einfacher.
(Ein paar Hinweise zur Fehlersuche:
- oft vergißt man einmal einen Strichpunkt am Ende eines Befehles (und er-
hält dann eine ganze Reihe von Fehlermeldungen, obwohl der Rest des Pro-
grammes richtig compiliert wird)
- das hervorgehobene Zeichen kann auch ein Punkt sein (was dann nicht immer
ohne weiteres erkennbar ist) - Anm.d.A.)
21. Quellen
^^^^^^^^^^^
Dieser Compiler wurde zu Lernzwecken geschrieben. Folgende Informationsquel-
len hat der Autor dabei benutzt:
Zunächst einmal wäre da das Amiga 1.3 "Native Developer´s Update", das ein
4 Disks umfassendes Paket von Commodore (genauergesagt von CATS). Es enthält
die kompletten C- und Assembler-Include-Libraries, amiga.lib, ALink und alle
möglichen anderen Sachen. Wichtiger war vielleicht, daß auch die Amiga-Auto-
docs darin enthalten sind, ein Satz Dokumentationsfiles, die jede Funktion
der Standard-Amiga-Libraries und -Devices beschreibt. Mit 20$ (US) sind die-
se jedoch ziemlich billig. Die Adresse, bei der Ihr das Teil bekommen könnt,
ist:
C.A.T.S
1200 Wilson Drive
West Chester, PA 19380
Außerdem benutzte der Autor die "Amiga ROM Kernel Manuals", die der offiziel-
le Buchsatz zur Amiga-Programmierung ist. Ohne diese wäre das Programmieren
wesentlich schwerer. Insbesondere das Intuition-Handbuch ist ziemlich wich-
tig. Auch sehr nützlich ist das Handbuch für Libraries und Devices. Diese
Bücher sind allerdings groß und sehr teuer... Manche behaupten auch, daß die-
se fehlerhaft und schwer zu verstehen sind; der Autor stellte aber fest, daß
sie doch relativ fehlerfrei und klar sind.
Als nächstes wäre Anders Bjerin´s C-Manual anzuführen. Es enthält eine Menge
über Intuition und Graphics. Außerdem sollte die C-Orientierung kein allzu
großes Problem darstellen. Es befindet sich übrigens auf den Fish-Disks 456
und 457 (entpackt erstreckt es sich auf 4 volle Disks!).
Folgende Quellen wurden vom Autor benutzt, um Informationen speziell über
Compiler zu bekommen:
1. PDC ist ein frei kopierbarer C-Compiler von Jeff Lydiatt. Dies ist ein
sehr gutes Programm, obwohl es in letzter Zeit von DICE, GCC und anderen
wirklich guten C-Compilern überholt wurde. Der Autor lernte vieles von
diesem Compiler und verwendete diesen auch häufig.
2. Pascal-S ist eine kleine Demonstration eines Pascal-p-Code-Compilers der
Elektro-Technischen-Hochschule Zürich. Der Sourcecode konnte ein paar
Fragen beantworten, ist aber echt kein gutes Beispiel f. Programmierstil.
3. Small-C ist ein weiterer frei kopierbarer C-Compiler, der zwar nicht sehr
mächtig ist, aber die Einfachheit des Sourcecodes macht es zu einer sehr
praktischen Referenz.
4. Ein Buch mit dem Titel "Brinch Hansen on Pascal Compilers" von Per Brinch
Hansen. Dieses Buch half dem Autor mehr, als alle anderen Bücher, die er
zu diesem Thema gelesen hat, während er den Compiler geschrieben hat. Von
diesem Buch bekam er vor allem gesagt, was er alles falsch gemacht hat...
5. Die "Toy-Compiler" Serie im "Amiga-Transactor" von Chris Gray. Diese Serie
ist sehr informativ, wobei Chris Gray, der Autor von Draco, hier das
Prinzip des Compilerbaus erklärt.
6. "Compiler: Principles, Techniques und Tools" von Aho, Sethi und Ullman.
Dies ist das große Buch, das sogenannte "Dragon-Buch", das letzte Wort
über das Compilerschreiben. Wenn Ihr etwas wissen wollt, ist´s schon
drin, aber mit einem dezenten mathemat. Hintergrund wäre man besser dran!
Obwohl das ein Textbuch für einen Kurs war, den der Autor gemacht hat,
hat er niemals mehr als ein paar Seiten davon gelesen...
(der Draco-Compiler von Chris Gray befindet sich auf den Fish-Disks 76 & 77
mit einem Update auf Fish 201, eine ältere Version des PDC-Compilers von
Jeff Lydiatt befindet sich auf der Fish-Disk 110 - Anm.d.Ü.)
22. Zukünftige Erweiterungen
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Der Autor benutzte V1.2 schon ein paar Monate vor der Veröffentlichung, wo-
durch er ziemlich sicher über seine Qualität ist. Es ist jedoch ein so großer
Unterschied seit der letzten Version, daß es eigentlich Probleme geben müßte.
Daher haben Fehler-Verbesserungen, wie immer, höchste Priorität.
Danach werden wohl optionale C-Calling-Conventions kommen, integriertes Peep-
hole-Optimieren und vielleicht IEEE-Single-Precesion-Reals.
Version 1.3 wird wohl eine Kompatibilitäts-Veröffentlichung sein. Darin ent-
halten sein werden Strings wie in Turbo, Funktionen, die Werte zurückgeben
können, double-precision-Reals und die Turbo-Typen Word, LongInt, ShortInt
usw. Aus Address wird wohl zu Pointer werden, Integer zu LongInt, Short zu
Integer, Exit zu Halt, usw. Vielleicht wird sogar Unit-Syntax eingebaut.
Das andere Gebiet, das der Autor gerne erforschen würde, wären Debugger,
vor allem Sourcelevel-Debugger.
23. Update-History
^^^^^^^^^^^^^^^^^^
(Sorry, aber ich hatte wirklich keinen Bock, die gesamte Update-History zu
übersetzen. Daher müßt ihr eben in der englischen Anleitung nachsehen! -
Anm.d.Ü.)
24. Weitere Anmerkungen, Copyright und Adressen
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Wie bereits erwähnt, liegt das Copyright für die Anleitung, für den Compiler
selbst, den Sourcecode und der Run-Time-Library bei:
Copyright © 1989 Patrick Quaid
Es ist erlaubt, das Paket frei zu vertreiben, solange alle Dateien (mit
Ausnahme des Linkers und des Assemblers; trotzdem bitte mitkopieren, falls
möglich) unverändert beigefügt sind. Niemand darf mit diesem Programm Geld
verdienen! Es darf nur auf Disk-Sammlungen weitergegeben werden, für die ein
vernünftiger Preis genommen wird (Maximum ist DM 5,- in Deutschland).
Sie können den Compiler jederzeit für Ihre eigenen Zwecke verändern, wenn
Sie wollen. Ich wäre Ihnen jedoch dankbar, wenn Sie mir dann, falls Sie
diese Version besser finden als das Original, eine Kopie zuschicken würden,
damit ich dies dann in die nächste Version mit einbinden kann. Besonders in-
teressiert ist der Autor im Bezug auf Verbesserungen in der Richtung von in-
tegrierten Umgebungen mittels ARexx oder so. Sollten Sie jedoch grundlegende
Dinge am Compiler verändern, die nicht mehr dem Standard von Pascal oder der
obigen Beschreibung entsprechen, dann geben Sie eine Kopie dieses modifizier-
ten Compilers bitte NICHT unter dem Namen PCQ weiter, da sonst nur Mißver-
ständnisse aufkommen.
Dies ist kein ShareWare-Produkt, d.h. Sie müssen kein schlechtes Gewissen
haben, falls nichts bezahlen, wenn Sie es verwenden. Wenn Sie mir wirklich
helfen wollen, dann schreiben Sie mir über Ihre Erfahrungen und vor allem
über die Fehler, die Sie entdeckt haben. Schreibt nicht über unimplementier-
ten Features - der Autor weiß über sie bescheid! Er versucht nicht den für
alle Zeiten besten Pascal-Compiler zu schreiben, aber er soll korrekt sein!
Sollten Ihr ein Bedürfnis verspüren, unbedingt Geld loswerden zu wollen, dann
schickt es an Charlie Gibbs, der den Assembler geschrieben hat oder an die
Software Distillery, die den Linker geschrieben haben.
Falls Ihr die aktuelle Version vom Autor haben wollt, schickt dem Autor 2,25$
(.50$ Umschlag, .75$ Porto, Disks 1$) bzw. nach Europa 3,00$ (das Porto ko-
stet hier um die 1,50$). Ein etwas höherer Betrag wäre für die Kostendeckung
des Autors recht nützlich.
Solltet Ihr irgendwelche Fragen, Kommentare oder was auch immer haben, dann
schreibet (in Englisch!) an:
Pat Quaid
8320 E.Redwing
Scottsdale, AZ 85250
USA
Tel.: (602) 967-3356
FidoNetz: Patrick Quaid @ 1:114/52
Ihr könnt den Autor eher per Post als per Telefon erreichen.
Haben Sie Spaß mit dem Compiler! Falls Sie Beschwerden haben, denken Sie an
den Preis! (Falls Sie etwas bezahlt haben, wenden Sie Sich an den, der das
Geld gekriegt hat!)
25. Stichwortverzeichnis
^^^^^^^^^^^^^^^^^^^^^^^^
Nachfolgend habe ich versucht, eine Übersicht über die im Anleitungstext be-
schriebenen Funktionen (mit Zeilenangabe) zu erstellen. Natürlich ist diese
Liste wahrscheinlich nicht vollständig, sie müßte aber alles wichtige enthal-
ten. Außerdem stimmen die Zeilennummern manchmal nicht ganz (+/- 5 Zeilen) -
Dieser Teil ist NICHT in der engl. Anleitung enthalten gewesen, sondern kom-
plett neu von mir erstellt worden. Der Übersetzer.
25.1 Allgemeine Begriffe
------------------------
A
bs() ......... 898
Address ....... 534
Adr() ......... 894
AllocString() . 2065
ArcTan() ...... 902
Array ......... 556,1792,1843,2085
Assignment .... 1512
B
it() ......... 905
Boolean ....... 529,1798,1857
Byte .......... 516,1780,1828
C
ase .......... 1041,1430
Char .......... 527,1790,1841
Ceil() ........ 912
Chr() ......... 908,2111
Close() ....... 756
CommandLine ... 597
Const ......... 411,1247
Cos() ......... 915
D
ec() ......... 761
Dispose() ..... 774
E
numerated .... 544
Eof() ......... 918
Exit .......... 779
ExitAddr ...... 617,2249
ExitCode ...... 611,2244
ExitProc ...... 605,2214
Exp() ......... 922
F
alse ......... 472
Float() ....... 925
Floor() ....... 931
For ........... 1051,1388
Function ...... 881,1062
G
et() ......... 796, 1906
Goto .......... 1569
H
eapErr ....... 622
HeapFunc ...... 2207
I
f ............ 1317
Inc() ......... 802
Input ......... 628
Integer ....... 510,1778,1826
IOResult ...... 934,1677,1934
L
n() .......... 943
M
axInt ........ 474,1042
MaxShort ...... 478
N
ew() ......... 812
Nil ........... 483
O
dd() ......... 947
Open() ........ 950,1740
Ord() ......... 959, 2453
Output ........ 635
P
red() ........ 964
Procedure ..... 399,649,743,1062,1524,2552
Program ....... 326,393,2517
Put() ......... 817,1920
R
ead() ........ 823,1815
ReadLn() ...... 829, 1873
Real .......... 525,1784,1836
Record ........ 262,1617
ReOpen() ...... 970,1709
Repeat ........ 1368
Reset() ....... 838,1685
Return ........ 1062,1551
Rewrite() ..... 849,1725
Round() ....... 978
S
hort ......... 512,1779,1827
Sin() ......... 983
SizeOf() ...... 987
Sqr() ......... 991
Sqrt() ........ 994
StdInName ..... 2000
StdOutName .... 2001
Strcpy() ...... 2077
Strdup() ...... 2072
String ........ 531,1795,1850
Succ() ........ 997
T
an() ......... 1002
Text .......... 538
Trap() ........ 859
True .......... 488
Trunc() ....... 1008,2457
Type .......... 261,395,441,505
V
ar ........... 393,570,1256
W
hile ......... 1346
With .......... 1466
Write() ....... 865,1762
WriteLn() ..... 872,1801
25.2 Mathematische Operatoren
-----------------------------
@ 1166
- 1168,1197
+ 1169,1196
* 1177
/ 1178
= 1206
<> 1207
> 1208
< 1210
>= 1212
<= 1214
and 1184
div 1180
mod 1182
not 1170
or 1198
shl 1185
shr 1187
xor 1199
25.3 Compiler-Direktiven
------------------------
{$A} 2340
{$B+|-} 2346
{$I""} 2365
{$I+|-} 1943,2370
{$O+|-} 2418
{$R+|-} 2423
{$SN|X|P|D} 2390
26. ABSCHLIESSENDE KURZINFORMATIONEN
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So, das wär´s nun endlich. Ich hoffe, Ihr blickt jetzt einigermaßen durch...
Wie bereits erwähnt, ist die engl. Anleitung nicht vollständig übersetzt (so
etwa zu 90 % müßte sie aber übersetzt sein). Außerdem wurde einiges zum bes-
seren Verständnis angefügt. Das Stichwortverzeichnis ist sowieso komplett von
mir erstellt worden. Die engl. Anleitung war außerdem nicht kapitelmäßig
durchnumeriert. Das habe ich angefügt, damit Ihr Euch besser zurechtfindet.
Bemerkungen zur dt. Übersetzung bitte an Stefan Grad, Bahnhofstr. 37, 78588
Denkingen, Deutschland. Die Adresse des Programmautors befindet sich im Kapi-
tel 24.
Alle Angaben in dieser Anleitung ohne Gewähr! Wenn Ihr damit irgendwas zer-
stört, können weder der Programmautor noch der Autor der deutschen Anleitung
dafür haftbar gemacht werden!
Programm-Name: PCQ-Pascal V1.2b
Programm-Autor: Patrick Quaid
Herkunft: Purity 3 und 6; Fish 503, 504 und 511
Dt. Anleitung: Stefan Grad (GPD) für Purity